tito10047/progressive-image-bundle 问题修复 & 功能扩展

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

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

tito10047/progressive-image-bundle

最新稳定版本:0.1.2

Composer 安装命令:

composer require tito10047/progressive-image-bundle

包简介

High-performance progressive image loading bundle for Symfony with Blurhash placeholders, zero CLS, and smart responsive strategies.

README 文档

README

Build Status PHP-CS-Fixer PHPStan Latest Stable Version License PHP Version Symfony Version Symfony Style Coverage Status

High-performance progressive image loading for Symfony.

Deliver lightning-fast user experiences by serving beautiful Blurhash placeholders while high-resolution images load in the background. This bundle simplifies responsive images with a Breakpoint-First approach (supporting standard aliases like sm, md, lg, xl) and features seamless LiipImagine integration for automatic thumbnail generation. It eliminates layout shifts (Zero CLS), boosts SEO with smart preload injection, and ensures upscale protection—all while maintaining a minimal memory footprint through stream-based metadata extraction.

<twig:pgi:Image src="images/hero.jpg" alt="Amazing Landscape" pointInterest="50x50" sizes="md-6@landscape xl-12@portrait">Image Not Found</twig:pgi:Image>

Progressive Image Preview

???? Key Features

Core Features

  • Smart Responsive Strategy: Breakpoint-First approach with built-in Upscale Protection. Never serve blurry upscaled images again.
  • Point of Interest (PoI) Cropping: Define a focal point (e.g., 75x25) to ensure the most important part of the image is always visible when cropped for different aspect ratios.
  • Smart Preload Injection: Automatically injects <link rel="preload"> tags or HTTP headers for hero images, boosting LCP scores by eliminating "indirect discovery".
  • Zero CLS (Cumulative Layout Shift): Automatically extracts and injects image dimensions to reserve space, ensuring a stable layout during loading.
  • Transparent HTML Caching: Optional caching of the rendered component HTML to avoid repeated Blurhash generation and metadata extraction, significantly improving response times for heavy pages.

Other Features

  • Smart Metadata Extraction: Uses PHP Streams to read only the necessary bytes for dimensions and hashes—no more loading 20MB images into RAM.
  • Modern Frontend Stack: Built on Symfony UX Twig Components and Stimulus for a seamless, reactive developer experience.
  • Cloud-Ready Architecture: Flexible LoaderInterface supports local files, network drives, and S3 (via custom loaders).
  • Advanced Path Resolution: Resolve images via Filesystem, AssetMapper, or a custom Chain resolver.
  • Developer Experience (DX): Simple <twig:pgi:Image> component with full support for custom attributes, filters, and decorators (e.g., LiipImagine).

???? Installation

Install the bundle via Composer:

composer require tito10047/progressive-image-bundle

If you are not using Symfony Flex, register the bundle manually:

// config/bundles.php return [ // ... Tito10047\ProgressiveImageBundle\ProgressiveImageBundle::class => ['all' => true], ];

⚙️ Configuration

This configuration is optional, create config/packages/progressive_image.yaml to configure your resolvers and loaders.

progressive_image: # Define how to locate your images resolvers: public_files: type: "filesystem" roots: ['%kernel.project_dir%/public'] allowUnresolvable: true assets: type: "asset_mapper" # Try multiple resolvers in order chain: type: "chain" resolvers: - 'public_files' - 'assets' # Global settings driver: "gd" # Image processor: "gd" or "imagick" loader: "progressive_image.filesystem.loader" # Service ID for loading file streams resolver: "chain" # Default resolver to use cache: "app.cache.progressive_image" # Recommended to use a persistent cache # Resolution for the generated Blurhash hash_resolution: width: 10 height: 8 # Transparent HTML Caching image_cache_enabled: false # Set to true to enable HTML caching image_cache_service: "cache.app" # The cache service to use ttl: 3600 # Cache TTL in seconds (null = default) # Integrations path_decorators: - "progressive_image.decorator.liip_imagine" # Enable LiipImagine support when@dev: progressive_image: resolver: chain

???? Usage

Simply use the provided Twig component in your templates. The component automatically handles the placeholder generation and Stimulus controller initialization.

{# Simple usage #} <twig:pgi:Image src="images/hero.jpg" alt="Amazing Landscape" /> {# Optimize LCP by preloading the image #} <twig:pgi:Image src="images/hero.jpg" preload /> {# With custom attributes and LiipImagine filter #} <twig:pgi:Image :context="{ 'filter': 'my_liip_filter' }" src="uploads/portrait.png" alt="User Profile" class="rounded-full shadow-lg" style="border: 2px solid #fff;" />

???? Smart Responsive Strategy

Stop managing magic pixel numbers. This bundle introduces a Breakpoint-First approach with built-in Upscale Protection.

1. Define your Grid

Configure your project's grid in a central configuration. You can use built-in frameworks like bootstrap or tailwind, or define your own.

# config/packages/progressive_image.yaml progressive_image: responsive_strategy: grid: framework: bootstrap # automatically sets 12 columns and standard breakpoints # OR custom: # framework: custom # columns: 12 # layouts: # xl: { min_viewport: 1200, max_container: 1140 } # md: { min_viewport: 768, max_container: 720 } # sm: { min_viewport: 0, max_container: null } # null means fluid (100vw)

2. Simple Grid-based Usage

In your Twig templates, use the sizes attribute to define how many columns the image should occupy at each breakpoint.

{# Image takes 12 columns on mobile (fluid) and 4 columns on desktop #} <twig:pgi:Image src="hero.jpg" sizes="sm-12 xl-4" /> {# You can also specify aspect ratios per breakpoint #} <twig:pgi:Image src="hero.jpg" sizes="sm-12@1-1 xl-4@16-9" />

The bundle automatically:

  1. Calculates the exact pixel width based on your grid configuration.
  2. Generates the srcset with optimized image sizes.
  3. Generates the sizes attribute (e.g., (min-width: 1200px) 380px, 100vw).
  4. Protects against upscaling: If the original image is smaller than the calculated size, it won't generate a blurry version.

3. Aspect Ratios

You can define global aspect ratios or use them directly in the grid parameter.

progressive_image: responsive_strategy: ratios: landscape: "16/9" portrait: "3/4" square: "1/1"

In Twig:

{# Use global ratio #} <twig:pgi:Image src="hero.jpg" sizes="md-6" ratio="landscape" /> {# Or override per breakpoint #} <twig:pgi:Image src="hero.jpg" sizes="sm-12@square md-6@landscape" />

4. Intelligence: Built-in Upscale Protection

The bundle never generates an image larger than the original source. For example, if your grid calculates a required width of 1200px but the original image is only 1000px:

  • The bundle filters out any variants larger than 1000px.
  • It ensures the browser never tries to download a 1200px upscaled (and thus blurry) version.

Result: No blurry upscaled images, saved CPU cycles, and reduced storage waste.

LiipImagine Integration

If you use LiipImagineBundle, this bundle integrates seamlessly to generate thumbnails on-the-fly.

By default, the bundle uses LiipImagineResponsiveImageUrlGenerator when LiipImagine is detected, which generates dynamic thumbnails based on the required dimensions.

# config/packages/progressive_image.yaml progressive_image: # This is often automatically configured if LiipImagine is present # path_decorators: # - "progressive_image.decorator.liip_imagine"

When using sizes, the bundle will automatically request the correct sizes from LiipImagine, using the original image's aspect ratio or the one specified in the sizes parameter. It uses a signed URL to safely generate any required thumbnail size.

Point of Interest (PoI)

You can specify a focal point for cropping using the pointInterest attribute. This is especially useful when the same image is cropped into different aspect ratios (e.g., landscape for desktop, square for mobile).

The value is a string in the format XxY, where X and Y are percentages (0-100) of the image width and height. For example, 50x50 is the center, 0x0 is the top-left corner, and 100x100 is the bottom-right corner.

<twig:pgi:Image src="hero.jpg" pointInterest="75x25" sizes="sm-12@1-1 xl-4@16-9" />

The bundle will automatically calculate the crop coordinates to keep the specified point as close to the center of the cropped image as possible, while ensuring the crop stays within the original image boundaries.

⚡ Smart Preload Injection (LCP Optimization)

One of the biggest challenges for Core Web Vitals (LCP) is the "Indirect Discovery" of images. If your hero image is hidden behind a component or managed by JavaScript, the browser's preload scanner won't find it fast enough.

This bundle solves this by implementing a Dependency Discovery Pattern:

  1. Collection: While Twig renders your components, the bundle automatically collects the URLs of images marked with the preload attribute.
  2. Injection: A Kernel Response Listener intercepts the final response and injects <link rel="preload"> tags directly into the HTML <head> (or as HTTP Link headers) before it's sent to the user.

Key Benefits:

  • Zero-Config: Just add the preload attribute, and the bundle handles the complex logic of moving links to the head.
  • Native Performance: Supports both HTML injection and HTTP/2 Link Headers for even faster delivery.
  • Smart Logic: Only preloads if preload attribute is present on the component.

⚡ Transparent HTML Caching

While this bundle is highly optimized for performance, generating Blurhashes and extracting metadata still takes some CPU cycles. On pages with dozens of images, this can add up.

Transparent HTML caching allows you to store the final rendered HTML of the <twig:pgi:Image> component in your cache (e.g., Redis or APCu).

How it works:

  1. When enabled, the bundle checks the cache before rendering the component.
  2. If a cached version exists, it returns it immediately, skipping all PHP logic (metadata extraction, Blurhash generation, path resolution).
  3. If not, it renders the component normally and stores the result in the cache for future requests.

The cache key is automatically generated based on all input properties passed to the component, ensuring that different images or filters never collide.

Configuration:

progressive_image: image_cache_enabled: true image_cache_service: 'cache.app' # Use any PSR-6 or PSR-16 cache service ttl: 86400 # Cache for 24 hours

????️ Architecture

Stream-based Approach

Unlike traditional tools that load the entire image into memory to get dimensions, this bundle uses PHP Streams. By interacting with the LoaderInterface, we can peek at the image headers to extract metadata (Width, Height, MIME type, and Blurhash).

This is particularly critical when handling:

  • Large Files: Avoid Memory Limit Exceeded errors on 50MB+ source files.
  • S3 / Network Drives: Only download the minimal required bytes over the wire instead of the full file, significantly reducing latency and bandwidth costs.

Extensibility

  • Loaders: Implement LoaderInterface to fetch images from any source (GCS, Azure Blob, External APIs).
  • Resolvers: Implement PathResolverInterface to customize how logical paths are mapped to physical locations.
  • Decorators: Modify the final image URL (e.g., adding CDN prefixes or image manipulation parameters).

???? License

The MIT License (MIT). Please see License File for more information.

统计信息

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

GitHub 信息

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

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-01-04