Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Tiny Png Bundle Laravel Package

setono/tiny-png-bundle

A Symfony bundle that integrates the TinyPNG API to automatically compress and optimize PNG and JPEG images, helping reduce file sizes and improve page load performance with simple configuration.

View on GitHub
Deep Wiki
Context7

Getting Started

First, a framing correction that still holds after v1.1.0: setono/tiny-png-bundle is a Symfony bundle, not a Laravel package. It ships no ServiceProvider, no Facade, no config/tiny-png.php, and is not registered via Laravel's package auto-discovery. composer require setono/tiny-png-bundle in a Laravel app will install code you cannot boot through the framework. v1.1.0 changed nothing about that — the release is a dependency bump ("updated Symfony and PHP"), not a feature or integration release. So your three realistic first moves are unchanged:

  1. Read it, don't install it. Clone the repo and open, in this order:
    • src/DependencyInjection/Configuration.php and .../SetonoTinyPngExtension.php — the actual config surface (almost certainly a single api_key under setono_tiny_png).
    • src/Service/TinyPng.php (or similarly named) — the thin wrapper around Tinify\Tinify::fromFile() / fromBuffer() / toFile(). This is the only logic you actually care about.
    • Resources/config/services.xml — constructor wiring and which interface to bind.
  2. Use the underlying SDK in Laravel. Add tinify/tinify directly and build a small App\Services\ImageOptimizer (or a TinyPngManager) mirroring the bundle's service. ~40 lines, full framework integration.
  3. If you must run the bundle, do it in a Symfony process (microservice, or a symfony/messenger worker) and call it over HTTP/queue. Still not worth it for this scope.

Worth checking before you decide: v1.1.0 bumped the Symfony and PHP constraints. That means the package may now install on a modern runtime where v1.0.4 would have failed composer update — but also that it may have dropped older Symfony/PHP support. Run composer why-not setono/tiny-png-bundle (or read composer.json directly) against your actual app before assuming either way.

Your first end-to-end use case: pick up a user avatar or product image after it is stored, compress it through Tinify, and write the result back to a public/s3 disk. Get a free API key from tiny.png (500 compressions/month) and put it in .env as TINIFY_API_KEY.

Implementation Patterns

The Laravel translation of the bundle's pattern. The bundle's job is: read config, construct a TinyPng service with the API key, expose compress($source, $destination). In Laravel:

// config/services.php
'tinify' => ['key' => env('TINIFY_API_KEY')],

// app/Services/ImageOptimizer.php
final class ImageOptimizer
{
    public function __construct(private readonly string $apiKey) {}

    public function compress(string $contents): string
    {
        \Tinify\Tinify::setKey($this->apiKey);
        return \Tinify\Tinify::fromBuffer($contents)->toBuffer();
    }
}

Bind it as a singleton in AppServiceProvider so Tinify::setKey() is set once per process rather than per call — mirrors what the Symfony DI container does. Nothing in v1.1.0 changes this contract: no new methods, no config keys, no interfaces.

Queue the work, always. Compression is a network round-trip to Tinify's servers. Never inline in a request or inside a store() call. Pattern: controller stores the original → dispatch OptimizeImageJob on a media queue → job reads from disk, compresses, writes the optimized variant, records path, bytes_saved, compression_count on the model. This is the Laravel equivalent of the bundle being consumed by a Symfony message handler.

Storage-shaped interface. Many Setono bundles take a Flysystem FilesystemOperator. If this one does, that maps 1:1 onto Laravel disks — Storage::disk('s3')->get($path) / ->put($outPath, $buffer). Keep originals and optimized copies on separate paths (originals/ vs optimized/) so you can re-run compression after a key rotation without destroying source data.

Compress from a buffer, not a URL. Tinify::fromBuffer() / fromFile() avoids an extra public URL or signed-link round trip and keeps private images private. Only use fromUrl() if the asset is already publicly addressable.

Idempotency keys. Store a hash of the original (sha256) plus a compressed_at timestamp on the model. The job should early-return if already compressed or if the stored original hasn't changed — Tinify's free tier is 500/month and you will burn through it on re-saves.

Gotchas and Tips

  • Ecosystem mismatch is the big one, and v1.1.0 did not fix it. No auto-discovery, no artisan vendor:publish, no facades, no Laravel support commit in the changelog. Anything in the README about config/packages/setono_tiny_png.yaml or services.yaml is Symfony-only. Treat the docs as design notes, not instructions.
  • Read the v1.1.0 diff before upgrading anything. The only substantive change is the Symfony/PHP constraint bump in composer.json. That is a maintenance release, not a feature release: no bug fixes to the Tinify wrapper, no new config, no security advisory. Two practical consequences: (a) if you're pulling this transitively through a Symfony app, re-run your test suite, because a raised PHP floor is a silent breaking change on older runtimes; (b) if you previously bounced off the version ceiling on PHP 8.2/8.3, try the constraint again — it may resolve now. In a Laravel app it still changes nothing, because you can't boot it regardless.
  • Still effectively dormant, just less so. v1.1.0 is the first release since Feb 2022 and it's a drive-by dependency bump by a first-time contributor, with zero dependents. Pin versions, read the source, and validate the tinify/tinify constraint yourself rather than trusting docs. If the constraint conflicts with your PHP runtime, use tinify/tinify standalone — that's not affected by this release at all.
  • Tinify error taxonomy. Catch Tinify\Exception\ClientException (4xx), AccountException (key invalid / verification pending / quota exceeded), ServerException (5xx, retryable), and ConnectionException (network, retryable). The bundle will not classify these for you. Map them to job $tries/backoff — retry only 5xx and connection errors; fail fast on AccountException.
  • Quota accounting. Tinify\Tinify::getCompressionCount() returns this month's usage. Log it in the job and alert (or flip a feature flag off) as you approach 500. Free tier is per-key, per-calendar-month, and resets — a scheduled check on the 1st is cheap insurance.
  • Compression is lossy and irreversible on the optimized copy. Keep the original and version output paths (optimized/{hash}/{filename}) so an algorithm change or a bad key doesn't strand you.
  • Privacy / egress. Every image is uploaded to Tinify's infrastructure. For GDPR, healthcare, or internal-only documents this is a non-starter — use spatie/image-optimizer with local pngquant/jpegoptim/cwebp. Treat Tinify as the "public marketing assets" path and local optimization as the default.
  • Memory. fromBuffer() holds the whole file in PHP memory; a 30 MB PNG can blow past memory_limit. Prefer fromFile() on a temp path and cap upload sizes before queuing.
  • setKey() is global static state on the SDK. In Octane or long-running workers, if you ever rotate keys or use multiple accounts, set it explicitly at the top of every job — never assume it persists correctly across requests.
  • Extension point worth copying: the bundle's TinyPng service is an interface-backed DI service, which is exactly the seam that lets you swap in a local optimizer later. In Laravel, define App\Contracts\ImageOptimizer and bind either TinyPngOptimizer or LocalOptimizer — one config flag, no call-site changes. This remains the single most valuable idea the bundle has to offer a Laravel codebase, and v1.1.0 doesn't add anything to it.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment