=== EAC Image Optimizer Pro ===
Contributors: emilioandres
Tags: webp, avif, image optimization, compress images, performance, lazy load
Requires at least: 5.6
Tested up to: 6.9
Requires PHP: 7.4
Stable tag: 2.0.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Compress, resize and convert your images to WebP & AVIF automatically — no external services, no monthly fees. Everything runs on your own server.

== Description ==

**Image Optimizer Pro** makes your WordPress images dramatically lighter without relying on any third‑party API or cloud service. It uses PHP's built‑in GD library to:

* **Resize** images that exceed a maximum dimension (configurable, default 1920px).
* **Compress** JPEGs to a sensible quality.
* **Generate WebP** versions of every image and thumbnail.
* **Generate AVIF** versions too (when your server runs PHP 8.1+ with AVIF support).
* **Serve** the modern format automatically via `.htaccess` rules — the browser gets AVIF or WebP if it supports it, and the original JPG/PNG otherwise.

It includes a full dashboard with real statistics, a bulk converter for your existing media library, automatic optimization of new uploads, a backup/restore system, configurable exclusions, and an event log.

= Key features =

* **WebP and AVIF** generation (AVIF requires PHP 8.1+).
* **Bulk optimization** of your existing library, processed in batches to avoid timeouts.
* **Automatic** optimization of new uploads.
* **Backup & restore** — keep a copy of every original before compressing, and restore them anytime.
* **Configurable** quality, max dimension, minimum file size, and which formats to generate.
* **Exclusions** — keep certain files (e.g. logos) from being converted, so they never break your layout.
* **Disk statistics** — see exactly how many files were converted and how much space you saved.
* **Clean orphans** — remove leftover WebP/AVIF files whose original was deleted.
* **System diagnostics** — verify GD, WebP/AVIF support, and `.htaccess` status at a glance.
* **No external services** — everything stays on your server. No accounts, no API keys, no recurring costs.

= How serving works =

The plugin writes rules to `wp-content/uploads/.htaccess` that, when a browser requests `photo.jpg`, transparently serve `photo.jpg.avif` or `photo.jpg.webp` instead — but only if the browser advertises support (via the `Accept` header) and the modern file exists. Older browsers always get the original. A `Vary: Accept` header is added so caches behave correctly.

If you use a CDN (e.g. Cloudflare on the free plan) that does not honour `Vary: Accept` for static files, pair this plugin with an HTML rewriter that outputs `<picture>` tags, or enable that behaviour in a compatible companion plugin.

== Installation ==

1. Upload the `image-optimizer-pro` folder to `/wp-content/plugins/`, or install the ZIP from **Plugins → Add New → Upload Plugin**.
2. Activate the plugin.
3. Go to **Image Optimizer** in the admin menu.
4. (Optional) Adjust quality, formats and exclusions in the **Settings** tab.
5. Click **Bulk optimize** to process your existing library.

== Frequently Asked Questions ==

= Does this upload my images anywhere? =
No. All processing happens on your own server using PHP GD. Nothing leaves your hosting.

= Will it break my logos or specific images? =
Add their filename (or part of it) to the **Exclusions** list in Settings. Excluded files never get a WebP/AVIF generated.

= Can I undo the compression? =
Yes, if **Backup originals** is enabled (it is by default). Use **Restore all originals** on the dashboard. Backups live in `wp-content/uploads/iop-backups/`.

= My server doesn't support AVIF =
AVIF needs PHP 8.1+ compiled with AVIF support in GD. The dashboard shows whether it's available. WebP works on virtually all modern PHP versions.

= Does it work with caching/CDN? =
Yes. The `.htaccess` rules add `Vary: Accept`. Some free CDN tiers ignore `Vary` for images; in that case use an HTML `<picture>` rewriter alongside this plugin.

== Development ==

The repository ships with a small PHPUnit suite that runs without WordPress.
It covers the pure helpers and transforms (companion-file classifier, SVG
optimiser) and includes a regression test for the v1.6.0 data-loss bug.

To run the tests:

1. Install dev deps: `composer install`
2. Run: `composer test`  (or `vendor/bin/phpunit`)

If Composer is not available, you can also point at a downloaded
`phpunit.phar`:

    php phpunit.phar --configuration phpunit.xml.dist

The `tests/`, `composer.json`, `composer.lock`, `phpunit.xml.dist` and
`vendor/` paths are NOT shipped in the distributable ZIP (handled by
`build.sh`).

== Changelog ==

= 2.0.0 =
* Renamed: plugin slug from `image-optimizer-pro` to `eac-image-optimizer-pro` to avoid collision with the unrelated commercial product of the same name. The folder, main file and Plugin Name change; the WP-CLI command, settings keys (`iop_settings`, `iop_history`, `iop_totals`), per-attachment meta (`_iop_optimized`, `_iop_stats`), text-domain and `.htaccess` marker are intentionally KEPT so your existing configuration and optimization history survive the upgrade.
* Identity: `Plugin Name` is now "EAC Image Optimizer Pro", `Author` is "Emilio Andrés Consulting", `Plugin URI` and `Author URI` point to real URLs.
* Upgrade path: deactivate and delete the old `image-optimizer-pro/` plugin, then install and activate this 2.0.0 ZIP from the WP admin. Your settings, history and `.htaccess` rules are preserved automatically.

= 1.6.2 =
* Added: PHPUnit test suite under `tests/Unit` (31 tests, 56 assertions). Includes a regression test that locks down the v1.6.0 data-loss bug — `IOP_Stats::is_companion_basename()` is exercised against every classification edge case so a future change cannot re-introduce the bug.
* Refactor: extracted `IOP_Stats::is_companion_basename($basename): bool` as a pure helper, used by `clean_orphans()` and `disk()` for consistency.
* Fixed: `IOP_Stats::disk()` no longer counts plain source `.webp` files in the "Saved" pairing calculation (same root cause as the v1.6.1 bug, but it only miscounted stats — not destructive). The reported savings ratio is now honest.
* Build: `build.sh` now excludes `tests/`, `composer.json`, `composer.lock`, `phpunit.xml*` and `vendor/` from the distributable ZIP.

= 1.6.1 — CRITICAL FIX =
* Fixed: `IOP_Stats::clean_orphans()` (and therefore `wp iop cleanup-orphans` and the admin "Clean orphan WebP/AVIF" button) deleted source `.webp` files that had been uploaded directly as WebP. The previous code stripped `.webp`/`.avif` and checked if the remaining name existed as a file — but for a source `foo.webp` there is no `foo` raster, so it was treated as an orphan and removed. The cleaner now only targets files matching the companion pattern (`*.jpg.webp`, `*.jpeg.webp`, `*.png.webp` and the `.avif` equivalents) — never plain `*.webp` source files. Affected users should restore from backup if they ran cleanup on 1.6.0 or earlier and used WebP source uploads.

= 1.6.0 =
* Added: SVG optimization. Strips XML comments, editor metadata (Adobe Illustrator, Inkscape, Sketch, Figma namespaces), empty groups and redundant whitespace. Conservative — only replaces the file if savings are at least 5%. Triggered on upload (`add_attachment` hook) and included in `wp iop bulk`.
* Added: `delete_attachment` hook. When an attachment is removed, the plugin now cleans up its backup, its `.webp`/`.avif` companion files and the modern companions of every subsize. Prevents orphan-file accumulation we observed in production.
* Added: `wp iop cleanup-orphans` command. Removes orphan `.webp`/`.avif` files (no original) AND orphan backups (no original) in one pass. Equivalent to the admin "Clean orphan WebP/AVIF" button but extended to backups.
* Added: `IOP_Backup::clean_orphans()` static method (public API for the above).

= 1.5.0 =
* Improved: `<picture>` HTML rewriter (toggle `Generate <picture> tags` in Settings) hardened for production use. Existing `<picture>`, `<noscript>` and `<svg>` blocks are now protected before any `<img>` is processed (no more accidental rewrites of lazy-load fallbacks or inline SVG icons).
* Added: request-scoped cache of `file_exists()` probes — repeated thumbnails on the same page are checked only once.
* Added: safety cap of 200 wrapped images per page to prevent runaway processing on edge-case pages.
* Added: `iop_picture_skip` filter (per-image opt-out) and `iop_picture_html` filter (final markup) for third-party integrations.
* Added: optional debug footprint via `?iop_debug` query param — appends an HTML comment with the wrapped image count, useful for validation before enabling globally.
* Added: REST API context guard.

= 1.4.0 =
* Added: Polished dashboard with a new "Performance" panel — hero card with the lifetime total saved, a three-segment progress bar showing optimized / skipped / pending, and an inline SVG chart of daily savings for the last 14 days.
* Added: Historical tracking. Each bulk run is recorded in a per-day bucket (`iop_history`, 90-day retention) and added to lifetime totals (`iop_totals`).
* Added: One-time recovery — if you upgrade from an earlier version with no totals, the dashboard seeds them from the existing `_iop_stats` per-attachment records.

= 1.3.0 =
* Added: WP-CLI commands `wp iop status`, `wp iop bulk`, `wp iop restore`, `wp iop purge-backups` for automation and cron jobs.
* Added: Native encoder support. If `cwebp` or `avifenc` binaries are available on the server, the plugin uses them (faster and smaller output) and falls back to GD automatically.
* Added: Auto-purge of old backups. New setting "Auto-delete old backups (days)" runs a daily WP-Cron task that removes backup files older than the configured number of days. 0 = never (default).
* Fixed: Statistics counters now include WebP and AVIF attachments in addition to JPEG/PNG, matching the formats the optimizer has processed since 1.1.0.

= 1.2.0 =
* Added: AVIF source files are now also re-encoded and resized (in addition to WebP, JPEG and PNG). Same conservative behaviour: only replaces the file when the new one is at least 5% smaller.
* Added: Spanish (es_ES) translation. POT template generated and a complete `image-optimizer-pro-es_ES.po`/`.mo` are bundled in `languages/`.

= 1.1.1 =
* Fixed: `_iop_stats` now reports the real "after" size — `clearstatcache()` is invoked after every in-place modification so cached `filesize()` values don't make `saved` look like 0.
* Improved: `compress_inplace` is now conservative — encodes to a temp file, compares sizes and only replaces the original when the new file is at least 5% smaller. Avoids degrading already well-compressed JPEG/WebP on repeated bulk runs.

= 1.1.0 =
* Added: WebP source files are now also re-encoded and resized by the bulk converter and on upload (previously only JPEG and PNG were processed). Files already in WebP are no longer skipped, so oversized WebP images can be optimized in place.
* Fixed: `compress_inplace` and `resize_inplace` now support WebP, using the configured `webp_quality` and preserving transparency.
* Fixed: `generate_modern` skips creating a WebP companion when the source is already WebP (no more `foo.webp.webp`).

= 1.0.0 =
* Initial release: WebP + AVIF generation, bulk converter, auto-optimize on upload, backup/restore, configurable settings & exclusions, disk statistics, system diagnostics, event log, and `.htaccess` serving rules.

== Upgrade Notice ==

= 2.0.0 =
The plugin slug changed (folder, main file and Plugin Name). Deactivate and delete the old "image-optimizer-pro" plugin, then install this ZIP. Your settings and optimization history are preserved automatically.

= 1.6.2 =
Adds a PHPUnit test suite and a defensive fix in the "Saved" stat calculation. No behaviour change for end users.

= 1.6.1 =
CRITICAL: fixes a data-loss bug in `clean_orphans` that could delete source `.webp` files. Upgrade immediately.

= 1.6.0 =
Adds SVG optimization and automatic cleanup of backups + modern companions when an attachment is deleted.

= 1.5.0 =
The `<picture>` rewriter is now production-ready (off by default). Enable it in Settings when you need WebP/AVIF served on a CDN that doesn't honour Vary: Accept.

= 1.4.0 =
Polished dashboard with lifetime savings total, optimization progress bar and a 14-day savings chart.

= 1.3.0 =
WP-CLI commands, native cwebp/avifenc encoders when available, auto-purge of old backups, and stats including WebP/AVIF.

= 1.2.0 =
Adds AVIF source optimization and a Spanish translation.

= 1.1.1 =
Fixes `saved=0` in stats and adds a conservative re-encode threshold so already-optimized files are not degraded.

= 1.1.0 =
WebP source files are now also optimized by the bulk converter and on upload.

= 1.0.0 =
First public release.
