snipershady/picdiet 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

snipershady/picdiet

Composer 安装命令:

composer require snipershady/picdiet

包简介

The lightweight image compressor for the web (JPG/WebP)

README 文档

README

Latest Version Total Downloads Monthly Downloads PHP Version License Tests codecov

Lightweight PHP library for compressing and converting images to WebP or JPEG format, with automatic resizing while preserving the aspect ratio.

Supports two backends: GD (built-in, zero extra dependencies) and Imagick (higher resampling quality, EXIF stripping, animated GIF support, AVIF/HEIC input).

Requirements

Requirement Version Notes
PHP >= 8.3
ext-gd * Required for the GD backend
ext-imagick * Optional — required only for the Imagick backend

Installing PHP extensions on Debian / Ubuntu

Replace php8.3 with your actual PHP version. Check it with php -v.

ext-gd

sudo apt update
sudo apt install php8.3-gd
sudo phpenmod gd
sudo systemctl restart php8.3-fpm   # or apache2, nginx, etc.

Verify:

php -m | grep gd

ext-imagick

Imagick requires both the PHP extension and the ImageMagick system library.

sudo apt update
sudo apt install imagemagick php8.3-imagick
sudo phpenmod imagick
sudo systemctl restart php8.3-fpm

Verify:

php -m | grep imagick

HEIC / HEIF support (optional): requires libheif and a version of ImageMagick compiled with HEIC support.

sudo apt install libheif-dev

Installation

composer require snipershady/picdiet

Quick start

Automatic backend selection (recommended)

createBest() picks Imagick if the extension is loaded, otherwise falls back to GD:

use PicDiet\Service\ImageCompressorFactory;

$service = ImageCompressorFactory::createBest();
$response = $service->compress('/path/to/photo.jpg');

if ($response->success) {
    echo $response->path;           // /path/to/photo_compressed.webp
    echo $response->originalSize;   // bytes
    echo $response->compressedSize; // bytes
} else {
    echo $response->error;
}

Explicit backend selection

use PicDiet\Enum\CompressionStrategy;
use PicDiet\Service\ImageCompressorFactory;

// GD backend (always available if ext-gd is installed)
$service = ImageCompressorFactory::factory(CompressionStrategy::GD);

// Imagick backend (throws RuntimeException if ext-imagick is not loaded)
$service = ImageCompressorFactory::factory(CompressionStrategy::IMAGICK);

Check backend availability at runtime

use PicDiet\Enum\CompressionStrategy;
use PicDiet\Service\ImageCompressorFactory;

if (ImageCompressorFactory::isAvailable(CompressionStrategy::IMAGICK)) {
    $service = ImageCompressorFactory::factory(CompressionStrategy::IMAGICK);
} else {
    $service = ImageCompressorFactory::factory(CompressionStrategy::GD);
}

API Reference

compress() — all parameters

$response = $service->compress(
    sourcePath:      '/path/to/image.png',    // required
    format:          ImageFormatEnum::WEBP,   // WEBP (default) or JPG
    maxWidth:        1920,                    // default 1920 px
    maxHeight:       1080,                    // default 1080 px
    quality:         85,                      // 0–100, default 85
    outputDirectory: '/path/to/output/',      // default: same directory as source
);
Parameter Type Default Description
$sourcePath string Absolute path to the source image
$format ImageFormatEnum WEBP Output format: WEBP or JPG
$maxWidth int 1920 Max output width in pixels. The image is never upscaled
$maxHeight int 1080 Max output height in pixels. Aspect ratio is always preserved
$quality int|null 85 Compression quality 0–100
$outputDirectory string|null same as source Directory where the compressed file is written. Must exist and be writable

Output filename: the compressed file is saved with the suffix _compressed appended to the original filename and the extension replaced with the chosen format.

/images/photo.jpg  →  /images/photo_compressed.webp
/images/banner.png →  /images/banner_compressed.jpg

CompressionResponse

All properties are readonly. The constructor is private: instances are only created via the named constructors CompressionResponse::success() and CompressionResponse::failure() inside the service layer.

Property Type Description
$success bool true if compression succeeded
$path string|null Absolute path to the compressed file (null on failure)
$error string|null Error message when $success is false, otherwise null
$originalSize int Source file size in bytes (0 on early failure before the file is read)
$compressedSize int Output file size in bytes (0 on failure)
$format ImageFormatEnum|null Format used for the output. Guaranteed non-null on success. On failure, null when the error occurs before the image is loaded (e.g. file not found); otherwise carries the intended format
$compressedFileName string|null File name of the compressed file (null on failure)
$outputDirectory string|null Directory where the compressed file was saved (null on failure)

CompressionStrategy

use PicDiet\Enum\CompressionStrategy;

CompressionStrategy::GD      // PHP GD extension
CompressionStrategy::IMAGICK // PHP Imagick extension

ImageFormatEnum

use PicDiet\Enum\ImageFormatEnum;

ImageFormatEnum::WEBP  // value: 'webp'
ImageFormatEnum::JPG   // value: 'jpg'

GD vs Imagick: choosing the right backend

Both backends implement the same interface and produce equivalent results in the most common scenarios (JPEG/PNG/WebP in, WebP/JPEG out). The choice depends on your infrastructure constraints and the quality bar you need to meet.

When to choose GD

  • Zero-friction setup. ext-gd ships with virtually every PHP distribution and most shared hosting plans. No system packages to install, no ImageMagick version to manage.
  • Minimal memory footprint. GD keeps everything in the PHP heap: predictable memory usage, easy to tune with memory_limit.
  • Enough for everyday web images. Thumbnail generation, upload pipelines, and CMS image processing rarely require the extra quality headroom that Imagick provides. GD handles these workloads well.
  • Shared or managed hosting. When you cannot install system packages (e.g. plain cPanel hosting), GD is the only viable option.

When to choose Imagick

  • Higher resampling quality. Imagick uses the Lanczos filter by default, which produces noticeably sharper results than GD's bicubic when downsizing to thumbnails or aggressive resolutions (e.g. 1200 px → 120 px). The difference is visible on product images, portfolio photos, and print-ready assets.
  • Automatic EXIF stripping. stripImage() removes all metadata (GPS coordinates, camera model, author, etc.) in a single call. With GD, metadata is silently preserved unless you handle it yourself. This matters for privacy-sensitive applications (medical imaging, user photo uploads).
  • Animated GIFs. GD can only read and write the first frame, silently discarding the animation. Imagick preserves all frames. If your application accepts GIF uploads, Imagick is the correct choice.
  • Wider input format support. Imagick can decode AVIF, HEIC/HEIF (iPhone default format), TIFF, BMP, and many others out of the box, provided the corresponding system libraries are present. GD is limited to JPEG, PNG, WebP, and GIF.
  • Large image processing. ImageMagick delegates heavy work to a native process, avoiding PHP's memory ceiling. Processing a 50 MP RAW-quality TIFF with GD will exhaust memory_limit; Imagick will not.
  • Professional / editorial workflows. Photo agencies, print pipelines, and e-commerce platforms with high visual standards typically require Lanczos-quality resampling and reliable EXIF removal. Imagick is the appropriate backend for these contexts.

Summary table

GD Imagick
Availability Built into most PHP packages Requires imagemagick system package
Installation complexity None apt install imagemagick php-imagick
Resampling algorithm Bicubic Lanczos (sharper results)
EXIF / metadata stripping No Yes (automatic)
Memory model PHP heap Delegated to ImageMagick process
Animated GIF input First frame only Full animation preserved
AVIF / HEIC input No Yes (requires system libs)
TIFF, BMP input No Yes
Best suited for Shared hosting, simple pipelines, cost-sensitive deployments High-quality image processing, privacy-sensitive apps, wide format support

Rule of thumb: start with createBest(). It automatically uses Imagick when available and falls back to GD otherwise, so your code is portable and improves silently as infrastructure grows.

Supported input formats

Format GD Imagick
JPEG yes yes
PNG (with transparency) yes yes
WebP yes yes
GIF (first frame only) yes yes
GIF (animated, all frames) no yes
AVIF no yes
HEIC / HEIF no yes (requires libheif)
TIFF no yes

Usage examples

Convert to WebP and measure savings

use PicDiet\Service\ImageCompressorFactory;

$service = ImageCompressorFactory::createBest();
$response = $service->compress('/var/www/uploads/photo.jpg');

if ($response->success) {
    $saved = $response->originalSize - $response->compressedSize;
    $ratio = (1 - $response->compressedSize / $response->originalSize) * 100;

    printf("Original:   %d bytes\n", $response->originalSize);
    printf("Compressed: %d bytes\n", $response->compressedSize);
    printf("Saved:      %d bytes (%.1f%% reduction)\n", $saved, $ratio);
    printf("Output:     %s\n", $response->path);
}

Convert PNG to WebP (preserving transparency)

GD and Imagick both handle PNG alpha channels correctly when converting to WebP, which natively supports transparency.

use PicDiet\Enum\ImageFormatEnum;
use PicDiet\Service\ImageCompressorFactory;

$service = ImageCompressorFactory::createBest();
$response = $service->compress('/var/www/uploads/logo.png', ImageFormatEnum::WEBP);

if ($response->success) {
    // logo_compressed.webp — fully transparent pixels preserved
    echo $response->path;
}

Convert to JPEG

use PicDiet\Enum\ImageFormatEnum;
use PicDiet\Enum\CompressionStrategy;
use PicDiet\Service\ImageCompressorFactory;

$service = ImageCompressorFactory::factory(CompressionStrategy::GD);
$response = $service->compress('/var/www/uploads/photo.png', ImageFormatEnum::JPG);

Resize to a maximum resolution

The image is scaled down proportionally so that neither dimension exceeds the given maximum. Images smaller than the maximum are never upscaled.

use PicDiet\Enum\ImageFormatEnum;
use PicDiet\Service\ImageCompressorFactory;

$service = ImageCompressorFactory::createBest();

// Resize to at most 1280×720, keeping aspect ratio
$response = $service->compress(
    sourcePath: '/var/www/uploads/photo.jpg',
    format:     ImageFormatEnum::WEBP,
    maxWidth:   1280,
    maxHeight:  720,
);

// A 1920×1080 source becomes 1280×720
// A 1920×800 source becomes 1280×533 (ratio preserved)
// A 640×480 source stays 640×480 (no upscaling)

Generate a thumbnail

use PicDiet\Enum\ImageFormatEnum;
use PicDiet\Service\ImageCompressorFactory;

$service = ImageCompressorFactory::createBest();
$response = $service->compress(
    sourcePath: '/var/www/uploads/photo.jpg',
    format:     ImageFormatEnum::WEBP,
    maxWidth:   300,
    maxHeight:  300,
    quality:    70,
);

Custom output directory

By default the compressed file is saved alongside the source. Pass outputDirectory to write it elsewhere. The directory must already exist.

use PicDiet\Enum\ImageFormatEnum;
use PicDiet\Service\ImageCompressorFactory;

$service = ImageCompressorFactory::createBest();
$response = $service->compress(
    sourcePath:      '/var/www/uploads/photo.jpg',
    format:          ImageFormatEnum::WEBP,
    outputDirectory: '/var/www/compressed/',
);

if ($response->success) {
    echo $response->outputDirectory; // /var/www/compressed/
    echo $response->path;            // /var/www/compressed/photo_compressed.webp
}

Custom quality

quality accepts values from 0 (maximum compression, lowest quality) to 100 (minimum compression, best quality). The default is 85, which is a good balance for most web use cases.

use PicDiet\Enum\ImageFormatEnum;
use PicDiet\Service\ImageCompressorFactory;

$service = ImageCompressorFactory::createBest();

// High quality — larger file, ideal for print or editorial
$response = $service->compress(
    sourcePath: '/var/www/uploads/photo.jpg',
    format:     ImageFormatEnum::WEBP,
    quality:    95,
);

// Low quality — smallest file, acceptable for previews and thumbnails
$response = $service->compress(
    sourcePath: '/var/www/uploads/photo.jpg',
    format:     ImageFormatEnum::WEBP,
    quality:    40,
);

Error handling

$response->success is always set. Check it before accessing output properties — they are null on failure.

use PicDiet\Service\ImageCompressorFactory;

$service = ImageCompressorFactory::createBest();
$response = $service->compress('/var/www/uploads/photo.jpg');

if (!$response->success) {
    // Possible error messages:
    //   'Source file does not exist'          — $format is null, $originalSize is 0
    //   'Failed to read source file size'     — $format is null, $originalSize is 0
    //   'Invalid image file'                  — GD backend; $format and $originalSize are set
    //   'Invalid image file, Exception: ...'  — Imagick backend; $format and $originalSize are set
    //   'Failed to create image resource'     — GD only; unsupported format (e.g. BMP)
    //   'Failed to save compressed image'     — write error; $format and $originalSize are set
    //   'Failed to save compressed image, Exception: ...' — Imagick backend write error
    //   'Failed to read compressed file size' — post-write stat error
    error_log('PicDiet compression failed: ' . $response->error);
    return;
}

// On success: $path, $compressedFileName, $outputDirectory and $format are guaranteed non-null
echo $response->path;
echo $response->compressedFileName;
echo $response->outputDirectory;
echo $response->format->value; // 'webp' or 'jpg'

Batch processing a directory

use PicDiet\Enum\ImageFormatEnum;
use PicDiet\Service\ImageCompressorFactory;

$service = ImageCompressorFactory::createBest();
$outputDir = '/var/www/compressed/';

foreach (glob('/var/www/uploads/*.{jpg,jpeg,png}', GLOB_BRACE) as $sourcePath) {
    $response = $service->compress(
        sourcePath:      $sourcePath,
        format:          ImageFormatEnum::WEBP,
        maxWidth:        1920,
        maxHeight:       1080,
        quality:         85,
        outputDirectory: $outputDir,
    );

    if ($response->success) {
        printf("OK  %s → %s (%d bytes saved)\n",
            basename($sourcePath),
            $response->compressedFileName,
            $response->originalSize - $response->compressedSize,
        );
    } else {
        printf("ERR %s: %s\n", basename($sourcePath), $response->error);
    }
}

Framework integration

Symfony

Register the interface in the container and bind it to the factory:

# config/services.yaml
services:
    PicDiet\Service\ImageCompressorInterface:
        factory: ['PicDiet\Service\ImageCompressorFactory', 'createBest']

Use it in a controller or service:

use PicDiet\Enum\ImageFormatEnum;
use PicDiet\Service\ImageCompressorInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Attribute\AsController;

#[AsController]
class ImageUploadController
{
    public function __construct(
        private readonly ImageCompressorInterface $compressor,
    ) {}

    public function __invoke(Request $request): void
    {
        $file = $request->files->get('image');
        $uploadPath = '/var/www/uploads/' . $file->getClientOriginalName();
        $file->move('/var/www/uploads', $file->getClientOriginalName());

        $response = $this->compressor->compress(
            sourcePath:      $uploadPath,
            format:          ImageFormatEnum::WEBP,
            maxWidth:        1920,
            maxHeight:       1080,
            outputDirectory: '/var/www/compressed/',
        );

        if (!$response->success) {
            throw new \RuntimeException($response->error);
        }

        // $response->path → absolute path to the compressed file
    }
}

Laravel

Bind the factory in a service provider, then inject it wherever needed:

// app/Providers/AppServiceProvider.php
use PicDiet\Service\ImageCompressorFactory;
use PicDiet\Service\ImageCompressorInterface;

public function register(): void
{
    $this->app->bind(ImageCompressorInterface::class, function () {
        return ImageCompressorFactory::createBest();
    });
}

Use it in a controller:

use PicDiet\Enum\ImageFormatEnum;
use PicDiet\Service\ImageCompressorInterface;
use Illuminate\Http\Request;

class ImageController extends Controller
{
    public function __construct(
        private readonly ImageCompressorInterface $compressor,
    ) {}

    public function upload(Request $request): void
    {
        $path = $request->file('image')->store('uploads');
        $fullPath = storage_path('app/' . $path);

        $response = $this->compressor->compress(
            sourcePath:      $fullPath,
            format:          ImageFormatEnum::WEBP,
            maxWidth:        1920,
            maxHeight:       1080,
            outputDirectory: storage_path('app/compressed'),
        );

        if (!$response->success) {
            abort(500, $response->error);
        }

        // $response->path → absolute path to the compressed file
    }
}

Custom implementation

Implement ImageCompressorInterface to plug in your own backend (e.g. a cloud API, libvips, or a mock for testing):

use PicDiet\Dto\CompressionResponse;
use PicDiet\Enum\ImageFormatEnum;
use PicDiet\Service\ImageCompressorInterface;

class MyCloudCompressor implements ImageCompressorInterface
{
    #[\Override]
    public function compress(
        string $sourcePath,
        ImageFormatEnum $format = ImageFormatEnum::WEBP,
        int $maxWidth = 1920,
        int $maxHeight = 1080,
        ?int $quality = null,
        ?string $outputDirectory = null,
    ): CompressionResponse {
        // Call your cloud API, write the output file, then return:
        return CompressionResponse::success(
            path:             '/tmp/output.webp',
            originalSize:     filesize($sourcePath),
            compressedSize:   1234,
            format:           $format,
            compressedFileName: 'output_compressed.webp',
            outputDirectory:  '/tmp',
        );
    }
}

Development

git clone https://github.com/snipershady/picdiet.git
cd picdiet
composer install
Command Description
composer test Run the PHPUnit test suite
composer phpstan Run PHPStan static analysis
composer cs-fix Fix code style with PHP-CS-Fixer
composer cs-check Check code style without applying changes
composer rector Run Rector refactoring
composer rector-dry Preview Rector changes without applying them
composer quality Run all quality tools (Rector + PHP-CS-Fixer)
composer quality-check Check all quality rules without applying changes

License

Released under the GPL-2.0-only license.

Author

Stefano Perrinispinfo.it

snipershady/picdiet 适用场景与选型建议

snipershady/picdiet 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 0 次下载、GitHub Stars 达 3, 最近一次更新时间为 2026 年 03 月 10 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「image-compression」 「picdiet」 「webpfy」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 snipershady/picdiet 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 snipershady/picdiet 我们能提供哪些服务?
定制开发 / 二次开发

基于 snipershady/picdiet 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 0
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 3
  • 点击次数: 29
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 3
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: GPL-2.0-only
  • 更新时间: 2026-03-10