Complete process-level reference for plugin engineers. Class hierarchies, call flows, DB schemas, hook signatures, API structures, and concurrency details.
Entry point, constants, DI container, service provider chain, and init sequence.
imagify.phpWordPress loads imagify.php during the plugins_loaded phase. It defines all constants and registers activation/deactivation hooks before delegating to inc/main.php.
// imagify.php — constants defined at plugin load time define( 'IMAGIFY_VERSION', '2.2.8' ); define( 'IMAGIFY_SLUG', 'imagify' ); define( 'IMAGIFY_FILE', __FILE__ ); define( 'IMAGIFY_PATH', realpath( plugin_dir_path( IMAGIFY_FILE ) ) . '/' ); define( 'IMAGIFY_URL', plugin_dir_url( IMAGIFY_FILE ) ); define( 'IMAGIFY_ASSETS_IMG_URL', IMAGIFY_URL . 'assets/images/' ); define( 'IMAGIFY_MAX_BYTES', 5242880 ); // 5 MB hard limit per image define( 'IMAGIFY_INT_MAX', PHP_INT_MAX - 30 ); define( 'IMAGIFY_SITE_DOMAIN', 'https://imagify.io' ); define( 'IMAGIFY_APP_DOMAIN', 'https://app.imagify.io' ); define( 'IMAGIFY_APP_API_URL', IMAGIFY_APP_DOMAIN . '/api/' );
imagify_init() lives in inc/main.php. It skips execution if DOING_AUTOSAVE is defined. The Plugin class (classes/Plugin.php) receives a League\Container instance and the plugin path, then orchestrates the full init sequence:
// classes/Plugin.php — init sequence (abridged) public function init( array $providers ): void { // 1. Register shared services $this->container->addShared( 'event_manager', fn() => new EventManager() ); $this->container->addShared( 'filesystem', fn() => new Imagify_Filesystem() ); // 2. Include procedural files (functions/, common/, 3rd-party/) $this->include_files(); // 3. Init legacy singletons Imagify_Auto_Optimization::get_instance()->init(); Imagify_Options::get_instance()->init(); Imagify_Data::get_instance()->init(); Imagify_Folders_DB::get_instance()->init(); Imagify_Files_DB::get_instance()->init(); Imagify_Cron_Library_Size::get_instance()->init(); Imagify_Cron_Rating::get_instance()->init(); Imagify_Cron_Sync_Files::get_instance()->init(); Imagify\Auth\Basic::get_instance()->init(); Imagify\Job\MediaOptimization::get_instance()->init(); Bulk::get_instance()->init(); // 4. Admin-only classes if ( is_admin() ) { ... } // 5. Register PSR-4 service providers + subscribers foreach ( $providers as $service_provider ) { $this->container->addServiceProvider( new $service_provider() ); $this->load_subscribers( $provider_instance ); } do_action( 'imagify_loaded', $this ); }
| Hook | Handler | What it does |
|---|---|---|
register_activation_hook | imagify_set_activation() | Sets transient imagify_activation with current user ID (TTL 30s). On network: set_site_transient. |
register_deactivation_hook | imagify_deactivation() | Deletes imagify_check_api_version and imagify_check_licence_1 site transients; fires imagify_deactivation action. |
init (Plugin) | Plugin::maybe_activate() | Reads activation transient; fires imagify_activation action with user ID, then deletes transient. |
Registers User singleton; binds account/quota service.
AdminBar, PluginFamily, AdminSubscriber.
AVIF rewrite-rule writers for Apache/Nginx/IIS.
CDN push integration.
Registers Picture\Display subscriber for <picture> tag rewriting.
Stat counters (e.g. OptimizedMediaWithoutNextGen).
WebP rewrite-rule writers.
GravityForms, Extendify; loads all inc/3rd-party/ integrations.
Media subscribers, upload handler.
Reset internal state tool, troubleshooting subscriber.
Composer autoload map, directory layout, and naming conventions.
composer.json)| Namespace prefix | Directory | Notes |
|---|---|---|
Imagify\ | classes/ | Primary PSR-4 root for all modern classes |
Imagify\Deprecated\Traits\ | inc/deprecated/Traits/ | Backward compat trait shims |
Imagify\ThirdParty\AS3CF\ | inc/3rd-party/amazon-s3-and-cloudfront/classes/ | S3 Offload integration |
Imagify\ThirdParty\EnableMediaReplace\ | inc/3rd-party/enable-media-replace/classes/ | Enable Media Replace compat |
Imagify\ThirdParty\FormidablePro\ | inc/3rd-party/formidable-pro/classes/ | Formidable Forms compat |
Imagify\ThirdParty\NGG\ | inc/3rd-party/nextgen-gallery/classes/ | NextGEN Gallery integration |
Imagify\ThirdParty\RegenerateThumbnails\ | inc/3rd-party/regenerate-thumbnails/classes/ | Regenerate Thumbnails compat |
Imagify\ThirdParty\WPRocket\ | inc/3rd-party/wp-rocket/classes/ | WP Rocket compat |
inc/classes/ and inc/deprecated/classes/ are loaded via Composer classmap. The convention is class-imagify-{name}.php → Imagify_{Name}. Two files are explicitly excluded from the classmap: class-imagify-plugin.php and class-imagify-requirements-check.php (loaded manually before autoloader is available).
classes/ Sub-namespacesBulk, BulkInterface, AbstractBulk, WP, CustomFolders, Noop
AbstractCommand, BulkOptimizeCommand, RestoreCommand, GenerateMissingNextgenCommand
ContextInterface, AbstractContext, WP, CustomFolders, Noop
MediaInterface, AbstractMedia, WP, CustomFolders, Noop
File, Process\{AbstractProcess, WP, CustomFolders, Noop}, Data\{AbstractData, WP, CustomFolders, Noop}
Display (output buffer rewriter)
MediaOptimization (background queue worker)
InternalStateList, ResetInternalState, Subscriber
InstanceGetterTrait (lightweight singleton), MediaRowTrait
static::get_instance(): static — a static singleton factory used by both PSR-4 classes and legacy Imagify_* classes. It stores the instance in static::$_instance.
Full inheritance chain from context factory to per-file API call.
ProcessInterface // classes/Optimization/Process/ProcessInterface.php └── AbstractProcess // classes/Optimization/Process/AbstractProcess.php (~2100 lines) ├── Process\WP // WP Media Library context ├── Process\CustomFolders // Custom Folders context └── Process\Noop // No-op fallback DataInterface // classes/Optimization/Data/DataInterface.php └── AbstractData ├── Data\WP // stores _imagify_data postmeta ├── Data\CustomFolders // stores in imagify_files table └── Data\Noop MediaInterface // classes/Media/MediaInterface.php └── AbstractMedia ├── Media\WP ├── Media\CustomFolders └── Media\Noop ContextInterface // classes/Context/ContextInterface.php └── AbstractContext ├── Context\WP ├── Context\CustomFolders └── Context\Noop
// inc/functions/common.php imagify_get_context( string $context ): ContextInterface imagify_get_optimization_process( int $media_id, string $context ): ProcessInterface // Context values: 'wp' | 'custom-folders' | 'ngg' (when NGG active) // Filterable via: imagify_context_class_name, imagify_process_class_name
| Method | Signature | Description |
|---|---|---|
__construct | (int|WP_Post|MediaInterface $id) | Accepts attachment ID, WP_Post, or MediaInterface object |
optimize | (?int $optimization_level, array $args = []): bool|WP_Error | Main entry for single-media optimization; acquires lock, iterates sizes |
reoptimize | (?int $optimization_level, array $args = []): bool|WP_Error | Restore then re-optimize at new level |
optimize_sizes | (array $sizes, ?int $level, array $args = []): bool|WP_Error | Push sizes to background job queue |
optimize_size | (string $size, ?int $level): bool|WP_Error | Optimize a single named size (e.g. 'full', 'thumbnail') |
optimize_missing_thumbnails | (): bool|WP_Error | Find and optimize sizes missing from postmeta |
restore | (): bool|WP_Error | Restore all sizes from backup; acquires restoring lock |
delete_backup | (): bool|WP_Error | Remove backup files for this media |
generate_nextgen_versions | (): bool|WP_Error | Generate WebP/AVIF variants for all optimized sizes |
delete_nextgen_files | (bool $keep_full = false, bool $all_next_gen = false): void | Remove WebP/AVIF sidecar files |
lock | (string $action = 'optimizing'): void | Set transient lock for 10 minutes |
unlock | (): void | Delete lock transient |
is_locked | (): string|false | Returns lock action string or false |
update_size_optimization_data | (object $response, string $size, int $level): void | Persist API response data for a size |
// Stored in _imagify_data['sizes'][$size_name] (WP context) // On success: [ 'success' => true, 'original_size' => int, // bytes before optimization 'optimized_size' => int, // bytes after optimization 'percent' => float, // savings percentage (2 decimal places) ] // On error: [ 'success' => false, 'error' => string, // human-readable error message ]
Low-level file operations: validation, resize, backup, API call, next-gen path generation.
Class: Imagify\Optimization\File — classes/Optimization/File.php (931 lines).
Injected with Imagify_Filesystem::get_instance(). Does not extend anything — purely compositional.
class File { protected string $path; // absolute path to file protected ?bool $is_image; // cached result of is_image() protected ?object $file_type; // {ext, type} from wp_check_filetype() protected Imagify_Filesystem $filesystem; protected mixed $editor; // WP_Image_Editor_Imagick|WP_Image_Editor_GD|WP_Error protected array $options; // cached get_imagify_option() calls public function __construct( string $file_path ) {...} }
| Method | Parameters → Return | Notes |
|---|---|---|
is_valid() | → bool | Returns true if $path is non-empty |
can_be_processed() | → true|WP_Error | Checks: path not empty, filesystem no errors, file exists, is a file, file writable, parent dir writable |
optimize(array $args) | → stdClass|WP_Error | Calls backup(), then upload_imagify_image(), downloads result, moves to destination |
resize(array $dimensions, int $max_width) | → string|WP_Error | Resizes via WP_Image_Editor; corrects EXIF orientation (cases 2–8); returns temp path |
create_thumbnail(array $destination) | → bool|array|WP_Error | Calls $editor->multi_resize(); moves to destination path |
backup(?string $backup_path, ?string $backup_source) | → true|false|WP_Error | Copies file to backup_path; also copies -scaled variant if exists |
is_exceeded() | → bool | Returns true if file size > IMAGIFY_MAX_BYTES (5 MB) |
is_supported(array $allowed_mime_types) | → bool | Checks MIME type against allow-list |
is_image() | → bool | MIME type starts with image/ |
is_pdf() | → bool | MIME type is application/pdf |
is_webp() | → bool | Regex: @(?!^|/|\)\.webp$@i — rejects bare .webp |
is_avif() | → bool | Same pattern for .avif |
get_path() | → string | Current absolute path (may change post-conversion) |
get_path_to_webp() | → string|false | Appends .webp to path; false if not an image or already WebP |
get_path_to_nextgen(string $format) | → string|false | Appends .webp or .avif; false if already next-gen |
get_mime_type() | → string | From cached wp_check_filetype() |
get_extension() | → string|false | File extension without dot |
get_dimensions() | → array{width:int, height:int} | Returns [0,0] if not image |
optimize( [ 'backup' => true, // false = skip backup regardless of user setting 'backup_path' => null, // string — explicit backup destination path 'backup_source' => null, // string — source to backup (WP 5.3+ original) 'optimization_level' => 0, // 0=normal/lossless, 1=aggressive, 2=ultra 'convert' => '', // 'webp' | 'avif' | '' for original format 'context' => 'wp', // sent to API for logging 'original_size' => 0, // bytes, sent to API ] );
HTTP transport, authentication, all endpoints, response schema, error handling.
Class: Imagify (legacy classmap) — inc/classes/class-imagify.php. Singleton via InstanceGetterTrait.
Base URL: IMAGIFY_APP_API_URL = https://app.imagify.io/api/
// Headers set in __construct() using stored API key $this->all_headers['Accept'] = 'Accept: application/json'; $this->all_headers['Content-Type'] = 'Content-Type: application/json'; $this->all_headers['Authorization'] = 'Authorization: token ' . $this->api_key; // upload_image() sends only Authorization header (multipart/form-data via cURL) // All other endpoints send all three headers
The private http_call() method auto-selects transport: if $args['post_data']['image'] is set, it routes to curl_http_call() (direct cURL for multipart file uploads); otherwise uses WordPress wp_remote_request(). A pre_imagify_request filter allows short-circuiting the cURL path.
| Method | Endpoint | HTTP | Body / Response |
|---|---|---|---|
get_user() | users/me/ | GET | JSON: {id, email, plan_id, plan_label, quota, extra_quota, extra_quota_consumed, consumed_current_month_quota, next_date_update, is_active, is_monthly} |
create_user($data) | users/ | POST | JSON body; no auth header |
update_user($data) | users/me/ | PUT | JSON body with all headers |
get_status($data) | status/{$data}/ | GET | Cached in static array per type |
get_api_version() | version/ | GET | 5s timeout; cached in site transient |
get_public_info() | public-info | GET | Marketing/public plan info |
upload_image($data) | upload/ | POST (cURL multipart) | $data = ['image' => $path, 'data' => json_encode($opts)]. Response: {image: $url, ...} |
fetch_image($data) | fetch/ | POST (JSON) | Optimize image from URL; same response shape as upload |
get_plans_prices() | pricing/plan/ | GET | Plan pricing objects |
get_all_prices() | pricing/all/ | GET | All pricing including packs |
check_coupon_code($coupon) | coupons/{$coupon}/ | GET | Coupon validity response |
check_discount() | pricing/discount/ | GET | Active discount info |
// $data array passed to upload_image() [ 'image' => '/absolute/path/to/image.jpg', // CURLFile in cURL transport 'data' => json_encode([ 'normal' => true/false, // level === 0 'aggressive' => true/false, // level === 1 'ultra' => true/false, // level === 2 'keep_exif' => true, 'original_size' => int, 'context' => string, // 'wp' | 'custom-folders' | 'ngg' 'convert' => string, // 'webp' | 'avif' — only when converting ]), ]
// Success — stdClass { "image": "https://app.imagify.io/...temp_url...", // URL for download_url() "original_size": 123456, "new_size": 98765, "percent": 19.87, // "message" key present when conversion was not possible (falls back to original) } // Error — WP_Error with code 'error {http_code}' // HTTP 401 → invalid API key // HTTP 413 → file too large // HTTP 4xx/5xx → $response->detail or $response->image error array
private function handle_response( string $response, int $http_code, string $error = '' ) { $response = json_decode( $response ); // stdClass or null if ( 200 !== $http_code && !empty( $response->code ) ) { // $response->detail → WP_Error message // $response->image → array of field errors return new WP_Error( 'error ' . $http_code, ... ); } if ( ! is_object( $response ) ) { return new WP_Error( 'not_valid_json', ... ); } return $response; }
get_user() and get_status() use 10s. get_api_version() uses 5s. All other calls default to 45s. Filterable via imagify_api_http_request_timeout.
Every key written to wp_postmeta by Imagify, with types and full schemas.
All stored on the attachment post (post_type = attachment). Managed by Imagify\Optimization\Data\WP.
| Meta Key | Type | Values / Schema |
|---|---|---|
_imagify_data |
Serialized array |
['sizes' => [...], 'stats' => [...], 'message' => string]See schema below. |
_imagify_status |
string | 'success' | 'already_optimized' | error string | '' (not optimized) |
_imagify_optimization_level |
int (stored as string) | 0 = normal/lossless, 1 = aggressive, 2 = ultra/smart |
_imagify_data Full Schema// _imagify_data serialized array structure [ 'sizes' => [ 'full' => [ 'success' => true, 'original_size' => int, 'optimized_size' => int, 'percent' => float ], 'thumbnail' => [ 'success' => true, ... ], 'medium' => [ 'success' => false, 'error' => string ], // ... one entry per registered image size + any custom sizes ], 'stats' => [ 'original_size' => int, // sum across all successful sizes 'optimized_size' => int, // sum across all successful sizes 'percent' => float, // aggregate % (2 decimal places) ], 'message' => string, // optional message from API (e.g. already optimized) ]
_imagify_status and _imagify_optimization_level are written only when the 'full' size is updated. They act as top-level fast-access keys mirroring _imagify_data['sizes']['full'].
| Meta Key | Modified When |
|---|---|
_wp_attachment_metadata | After resize (adds/removes sizes array entries); after thumbnail generation (updates width/height); after WP 5.3 original file handling |
_wp_attached_file | Not directly modified; read to resolve absolute paths |
All values stored under a single serialized option. Class: Imagify_Options (inc/classes/class-imagify-options.php).
Option name: imagify_settings (single site) or imagify_settings stored via get_site_option (network). Set via get_imagify_option($key) / update_imagify_option($key, $value).
| Key | Type | Default | Reset Value | Description |
|---|---|---|---|---|
api_key | string | '' | — | Imagify API key. Overridable via PHP constant IMAGIFY_API_KEY. |
optimization_level | int | 2 | 2 | 0=lossless, 1=aggressive, 2=ultra |
lossless | int (bool) | 0 | — | Force level 0 for all optimizations |
auto_optimize | int (bool) | 0 | 1 | Auto-optimize on upload |
backup | int (bool) | 0 | 1 | Keep backup of originals |
resize_larger | int (bool) | 0 | 1 (if WP 5.3+) | Resize images larger than threshold |
resize_larger_w | int | 0 | From big_image_size_threshold filter (default 2560) | Max width in pixels for resize |
display_nextgen | int (bool) | 0 | — | Enable next-gen format delivery |
display_nextgen_method | string | 'picture' | — | 'picture' = HTML rewrite; 'rewrite' = server-side rules |
display_webp | int (bool) | 0 | — | Legacy WebP delivery toggle |
display_webp_method | string | 'picture' | — | Legacy WebP method selector |
cdn_url | string | '' | — | CDN base URL for URL→path resolution |
disallowed-sizes | array | [] | — | Size names excluded from optimization |
admin_bar_menu | int (bool) | 1 | 1 | Show Imagify in admin bar |
partner_links | int (bool) | 0 | 1 | Show partner links in plugin UI |
convert_to_avif | int (bool) | 0 | — | Generate AVIF sidecar files |
convert_to_webp | int (bool) | 0 | — | Generate WebP sidecar files |
optimization_format | string | 'webp' | — | 'webp' | 'avif' | 'off' |
reset_values array in Imagify_Options contains only keys that differ from defaults; it is applied on first install or explicit reset. The option is a single serialized blob — never stored as individual keys.
Complete DDL for all three custom tables: imagify_folders, imagify_files, and ngg_imagify_data.
Class: Imagify_Folders_DB (inc/classes/class-imagify-folders-db.php). Global table in multisite ($wpdb->base_prefix). Table version: 100.
CREATE TABLE `{prefix}imagify_folders` ( `folder_id` bigint(20) unsigned NOT NULL auto_increment, `path` varchar(191) NOT NULL default '', `active` tinyint(1) unsigned NOT NULL default 0, PRIMARY KEY (folder_id), UNIQUE KEY path (path), KEY active (active) );
| Column | Type | Description |
|---|---|---|
folder_id | bigint unsigned PK | Auto-increment primary key |
path | varchar(191) UNIQUE | Absolute path with placeholder: {{ROOT}}/wp-content/uploads/gallery/. Uses {{ROOT}} and {{ABSPATH}} tokens for portability. |
active | tinyint(1) | 1 = selected in settings; 0 = deactivated. Indexed for fast active-folder queries. |
Class: Imagify_Files_DB (inc/classes/class-imagify-files-db.php). Global table in multisite. Table version: 102.
CREATE TABLE `{prefix}imagify_files` ( `file_id` bigint(20) unsigned NOT NULL auto_increment, `folder_id` bigint(20) unsigned NOT NULL default 0, `file_date` datetime NOT NULL default '0000-00-00 00:00:00', `path` varchar(191) NOT NULL default '', `hash` varchar(32) NOT NULL default '', -- MD5 of file `mime_type` varchar(100) NOT NULL default '', `modified` tinyint(1) unsigned NOT NULL default 0, `width` smallint(2) unsigned NOT NULL default 0, `height` smallint(2) unsigned NOT NULL default 0, `original_size` int(4) unsigned NOT NULL default 0, `optimized_size` int(4) unsigned default NULL, `percent` smallint(2) unsigned default NULL, `optimization_level` tinyint(1) unsigned default NULL, `status` varchar(20) default NULL, `error` varchar(255) default NULL, `data` longtext default NULL, -- serialized, see below PRIMARY KEY (file_id), UNIQUE KEY path (path), KEY folder_id (folder_id), KEY optimization_level (optimization_level), KEY status (status), KEY modified (modified) );
| Column | Notes |
|---|---|
folder_id | FK reference to imagify_folders.folder_id (not enforced at DB level) |
path | Absolute path using same {{ROOT}} tokens as folders table |
hash | MD5 hash of file contents — used by refresh_file() to detect modifications |
modified | 1 when file has changed since last optimization (hash mismatch) |
status | 'success' | 'already_optimized' | 'error' | NULL (not yet processed) |
data | Serialized array — same shape as _imagify_data (sizes + stats) |
Class: Imagify\ThirdParty\NGG\DB (inc/3rd-party/nextgen-gallery/classes/DB.php). Per-site table ($wpdb->prefix). Table version: 100.
CREATE TABLE `{prefix}ngg_imagify_data` ( `data_id` bigint(20) unsigned NOT NULL auto_increment, `pid` bigint(20) unsigned NOT NULL default 0, `optimization_level` varchar(1) NOT NULL default '', `status` varchar(30) NOT NULL default '', `data` longtext default NULL, PRIMARY KEY (data_id), KEY pid (pid) );
pid is the NextGEN picture ID. data is serialized the same way as _imagify_data.
All three DB classes extend Imagify_Abstract_DB which implements Imagify\DB\DBInterface. It provides:
maybe_upgrade_table() — create/upgrade on plugin initcreate_table() — issues dbDelta()can_operate(): bool — true when table is ready{option_prefix}_db_versionget($id), get_by($col, $val), get_in($col, $vals)get_var($col, $where), get_column_in($col, $ids)insert($data), update($data, $where), delete($id)serialize_columns()cast_row() based on column type mapHow bulk jobs are enqueued, tracked, and completed via ActionScheduler async actions.
Class: Imagify\Bulk\Bulk (classes/Bulk/Bulk.php). Singleton. Registered hooks in init().
// Bulk::run_optimize() — one as_enqueue_async_action() per media as_enqueue_async_action( 'imagify_optimize_media', [ 'id' => (int) $media_id, 'context' => (string) $context, // 'wp' | 'custom-folders' 'level' => (int) $optimization_level, ], "imagify-{$context}-optimize-media" // group name — allows cancellation per context ); // Next-gen generation uses a separate hook as_enqueue_async_action( 'imagify_convert_next_gen', [ 'id' => $media_id, 'context' => $context ], "imagify-{$context}-convert-nextgen" );
| Transient | Set When | Shape | TTL |
|---|---|---|---|
imagify_wp_optimize_running | Start of WP library bulk run | ['total' => int, 'remaining' => int] | DAY_IN_SECONDS |
imagify_custom-folders_optimize_running | Start of custom-folders bulk run | ['total' => int, 'remaining' => int] | DAY_IN_SECONDS |
imagify_bulk_optimization_result | After each successful optimization | ['total' => int, 'original_size' => int, 'optimized_size' => int] | DAY_IN_SECONDS |
imagify_bulk_optimization_complete | When remaining reaches 0 | 1 | DAY_IN_SECONDS |
imagify_missing_next_gen_total | Start of next-gen generation run | int (total count) | HOUR_IN_SECONDS |
imagify_bulk_optimization_infos | User dismisses info popup | 1 | WEEK_IN_SECONDS |
ActionScheduler is bundled at inc/Dependencies/ActionScheduler/action-scheduler.php. Jobs go through these states:
On failure: failed. On cancel: canceled. The check_optimization_status() hook fires on imagify_after_optimize and decrements the running counter, deleting the transient and setting the complete transient when all jobs finish.
// Bulk::get_contexts() — determines which contexts appear on bulk page if ( ! is_network_admin() ) { $types['library|wp'] = 1; // library only in site admin } if ( imagify_is_active_for_network() && is_network_admin() ) { $types['custom-folders|custom-folders'] = 1; // custom folders in network admin } elseif ( ! imagify_is_active_for_network() ) { $types['custom-folders|custom-folders'] = 1; // custom folders in site admin }
Transient-based per-media locks that prevent duplicate concurrent optimization or restore jobs.
Defined in AbstractProcess. Each lock is a transient named after the context and media ID.
// Transient name pattern (LOCK_NAME constant) const LOCK_NAME = 'imagify_%1$s_%2$s_process_locked'; // Example: 'imagify_wp_42_process_locked' // Example: 'imagify_custom-folders_7_process_locked' // Network-aware: uses set_site_transient when context is_network_wide() public function lock( string $action = 'optimizing' ): void { $name = $this->get_lock_name(); // sprintf(LOCK_NAME, ctx, id) $callback = $media->get_context_instance()->is_network_wide() ? 'set_site_transient' : 'set_transient'; call_user_func( $callback, $name, $action, 10 * MINUTE_IN_SECONDS ); } public function is_locked(): string|false { // Returns 'optimizing' | 'restoring' | false $callback = ...'get_site_transient' or 'get_transient'...; $action = call_user_func( $callback, $name ); return $this->validate_lock_action( $action ); // normalizes 'restore' → 'restoring' } public function unlock(): void { $callback = ...'delete_site_transient' or 'delete_transient'...; call_user_func( $callback, $name ); }
| Value | Set By | Cleared By |
|---|---|---|
'optimizing' | optimize() before iterating sizes | optimize() after all sizes complete |
'restoring' | restore() at start | restore() at end (success or error) |
Managed by Imagify\Tools\InternalStateList::get_locked_transient_patterns(). Used by ResetInternalState for SQL LIKE deletion:
'_transient_%imagify-auto-optimize-%' // Legacy (deprecated) '_transient_%imagify_rpc_%' // Legacy (deprecated) '_transient_imagify_%_process_locked' // Active single-site locks '_site_transient_imagify_%_process_lock%' // Active network-wide locks
Reset Internal State admin tool can force-clear all locks immediately via direct SQL DELETE.
How <img> tags are rewritten to <picture> tags at the HTTP response level.
Class: Imagify\Picture\Display (classes/Picture/Display.php). Implements SubscriberInterface.
public static function get_subscribed_events(): array { return [ 'template_redirect' => 'start_content_process', 'imagify_process_webp_content' => 'process_content', ]; }
start_content_process()get_imagify_option('display_nextgen') must be truthyget_imagify_option('display_nextgen_method') must equal 'picture' (Display::OPTION_VALUE)imagify_allow_picture_tags_for_nextgen must return trueThe parser checks these src attributes in priority order: data-lazy-src → data-src → src. Likewise for srcset: data-lazy-srcset → data-srcset → srcset. The generated <source> tag mirrors whichever attribute was active.
<!-- Input --> <img src="/uploads/photo.jpg" srcset="/uploads/photo-300.jpg 300w" sizes="..." alt="..."> <!-- Output (when both AVIF and WebP exist) --> <picture> <source type="image/avif" srcset="/uploads/photo.jpg.avif, /uploads/photo-300.jpg.avif 300w" sizes="..."> <source type="image/webp" srcset="/uploads/photo.jpg.webp, /uploads/photo-300.jpg.webp 300w" sizes="..."> <img src="/uploads/photo.jpg" srcset="/uploads/photo-300.jpg 300w" sizes="..." alt="..."> </picture>
url_to_path() converts image URLs to filesystem paths for existence checks. It handles: uploads URL, site root URL, CDN URL (via imagify_cdn_source_url filter), and protocol-relative URLs. Static caches are maintained per request.
| Filter | Signature | Purpose |
|---|---|---|
imagify_allow_picture_tags_for_nextgen | (bool $allow): bool | Global on/off switch for the rewriter |
imagify_webp_picture_images_to_display | (array $images, string $content): array | Filter/add/remove images before rewriting |
imagify_webp_picture_process_image | (array $data, string $img_tag): array|false | Per-image data manipulation (used by S3 Offload integration) |
imagify_picture_attributes | (array $attributes, array $data): array | Attributes on the <picture> element |
imagify_picture_source_attributes | (array $attributes, array $data): array | Attributes on each <source> element |
imagify_picture_img_attributes | (array $attributes, array $data): array | Attributes on the fallback <img> |
imagify_additional_source_tags | (string $html, array $data): string | Inject extra <source> elements before the generated ones |
imagify_buffer | (string $buffer): string | Final buffer after all replacements |
imagify_cdn_source_url | (string $url): string | CDN base URL for URL-to-path mapping |
Every wp_ajax_* and admin_post_* action with its nonce name and capability requirement.
Security is enforced by imagify_check_nonce($action, $query_arg) which wraps check_ajax_referer() and calls imagify_die() on failure. Capability checks use imagify_get_context($ctx)->current_user_can($capability, $media_id).
These are registered for both AJAX and form POST. Handler class: Imagify_Admin_Ajax_Post.
| Action | Nonce Name | Capability | Description |
|---|---|---|---|
imagify_manual_optimize | imagify-optimize-{id}-{ctx} | manual-optimize | Optimize single attachment |
imagify_manual_reoptimize | imagify-manual-reoptimize-{id}-{ctx} | manual-optimize | Re-optimize at different level |
imagify_optimize_missing_sizes | imagify-optimize-missing-sizes-{id}-{ctx} | manual-optimize | Generate missing thumbnail sizes |
imagify_generate_nextgen_versions | imagify-generate-nextgen-versions-{id}-{ctx} | manual-optimize | Generate WebP/AVIF for one attachment |
imagify_delete_nextgen_versions | imagify-delete-nextgen-versions-{id}-{ctx} | manual-restore | Remove WebP/AVIF sidecar files |
imagify_restore | imagify-restore-{id}-{ctx} | manual-restore | Restore attachment from backup |
imagify_optimize_file | imagify_optimize_file | manual-optimize (custom-folders ctx) | Optimize custom folder file |
imagify_reoptimize_file | imagify_reoptimize_file | manual-optimize (custom-folders ctx) | Re-optimize custom folder file |
imagify_restore_file | imagify_restore_file | manual-restore (custom-folders ctx) | Restore custom folder file from backup |
imagify_refresh_file_modified | imagify_refresh_file_modified | manual-optimize (custom-folders ctx) | Refresh file hash/modified status |
| Action | Nonce Name | Capability / Check | Description |
|---|---|---|---|
imagify_bulk_optimize | imagify-bulk-optimize | bulk-optimize | Launch ActionScheduler bulk job |
imagify_missing_nextgen_generation | imagify-bulk-optimize | bulk-optimize per context | Generate all missing next-gen files |
imagify_get_folder_type_data | imagify-bulk-optimize | bulk-optimize | Stats for one folder type on bulk page |
imagify_bulk_info_seen | imagify-bulk-optimize | bulk-optimize | Set imagify_bulk_optimization_infos transient |
imagify_bulk_get_stats | imagify-bulk-optimize | bulk-optimize per folder type | Aggregate bulk page statistics |
imagify_reset_internal_state | imagify_reset_internal_state | manage (wp ctx) | Clear all locks, transients, AS jobs |
imagify_check_backup_dir_is_writable | imagify_check_backup_dir_is_writable | manage (wp ctx) | Test backup directory writability |
imagify_get_files_tree | get-files-tree | manage (custom-folders ctx) | Filesystem tree for folder picker |
imagify_signup | imagify-signup (imagifysignupnonce) | manage (wp ctx) | Create Imagify account |
imagify_check_api_key_validity | imagify-check-api-key (imagifycheckapikeynonce) | manage | Validate API key against API |
imagify_get_prices | imagify_get_pricing_{user_id} (imagifynonce) | manage | Fetch plan prices |
imagify_check_coupon | imagify_get_pricing_{user_id} (imagifynonce) | manage | Validate coupon code |
imagify_get_discount | imagify_get_pricing_{user_id} (imagifynonce) | manage | Check active discount |
imagify_get_images_counts | imagify_get_pricing_{user_id} (imagifynonce) | manage | Count images per status |
imagify_update_estimate_sizes | update_estimate_sizes | manage | Recalculate size estimates |
imagify_get_user_data | imagify_get_user_data | manage | Fetch fresh account data from API |
imagify_delete_user_data_cache | imagify_delete_user_data_cache | manage | Purge cached user data transient |
nopriv_imagify_rpc | imagify_rpc_{rpc_id} (imagify_rpc_nonce) | None (nonce only) | Internal RPC dispatch — re-fires as wp_ajax_{action} |
| Action | Nonce Name | Capability |
|---|---|---|
imagify_scan_custom_folders | imagify_scan_custom_folders | optimize (custom-folders ctx) |
imagify_dismiss_ad | imagify-dismiss-ad | manage (wp ctx) |
imagify_dismiss_notice | imagify-dismiss-notice | Varies per notice |
imagify_deactivate_plugin | imagify-deactivate-plugin | Varies per notice |
imagify_rollback | imagify_rollback | manage_options |
{id} and {ctx} are unique per media item and context (e.g. imagify-optimize-42-wp). This prevents CSRF replay across different media items.