# How the Mass Delete Action in OpenMage Image Cleaner Handles Missing Files Safely

> OpenMage Image Cleaner mass delete safely skips missing files with file_exists() and unlink() avoiding PHP warnings. Clean stale database records automatically.

- Repository: [Fabrizio Balliano/openmage-image-cleaner](https://github.com/fballiano/openmage-image-cleaner)
- Tags: how-to-guide
- Published: 2026-03-01

---

**The mass delete action in OpenMage Image Cleaner safely skips files that no longer exist on disk by checking `file_exists()` before attempting `unlink()`, automatically cleaning up stale database records without throwing PHP warnings.**

The `fballiano/openmage-image-cleaner` extension provides a robust mechanism for purging unused images from your OpenMage (Magento 1) installation. When administrators select multiple entries in the Unused Images grid and execute a bulk deletion, the controller implements defensive checks to handle scenarios where physical files may have been removed externally or corrupted, ensuring the database remains synchronized with the filesystem state.

## How the Mass Delete Action Processes Files

The deletion logic resides in `FbimagecleanerController::massDeleteAction()` within [`app/code/community/Fballiano/ImageCleaner/controllers/Adminhtml/FbimagecleanerController.php`](https://github.com/fballiano/openmage-image-cleaner/blob/main/app/code/community/Fballiano/ImageCleaner/controllers/Adminhtml/FbimagecleanerController.php). The controller receives an array of image IDs via the `ids` parameter and processes each entry through a four-step safety workflow.

### Loading Database Records and Resolving Paths

For each image ID submitted by the admin grid, the controller first retrieves the corresponding database record and constructs the absolute filesystem path:

```php
$image = $db->fetchRow("SELECT * FROM {$cleaner_table} WHERE image_id=?", $image_id);
$image_path = $helper->getMediaDirByEntityTypeId($image['entity_type_id']) . $image["path"];

```

The helper method `getMediaDirByEntityTypeId()` (defined in [`Helper/Data.php`](https://github.com/fballiano/openmage-image-cleaner/blob/main/Helper/Data.php)) maps entity types to their correct media directories, ensuring the system targets the right physical location regardless of whether the image belongs to products, categories, or other entities.

### The Critical File Existence Check

Before attempting any filesystem operation, the code explicitly verifies the file's presence:

```php
if (!file_exists($image_path)) {
    $db->query("DELETE FROM {$cleaner_table} WHERE image_id=?", $image_id);
    continue;
}

```

This check prevents PHP from generating warnings or fatal errors when calling `unlink()` on non-existent paths. **If the file is missing**, the controller bypasses the deletion attempt and immediately removes the stale database row, maintaining referential integrity between the cleaner table and the actual filesystem state.

### Safe Deletion and Error Handling

When a file exists, the controller attempts deletion and implements single-error reporting to avoid UI flooding:

```php
if (unlink($image_path)) {
    $db->query("DELETE FROM {$cleaner_table} WHERE image_id=?", $image_id);
} elseif (!$error_message_thrown) {
    $error_message_thrown = true;
    Mage::getSingleton('adminhtml/session')
        ->addError($this->__('It was not possible to delete one or more files from the filesystem.'));
}

```

The `$error_message_thrown` boolean flag ensures administrators see only one error message per batch operation, even if multiple files fail to delete due to permissions or other filesystem constraints.

## Code Implementation Deep Dive

The complete mass delete action implementation demonstrates enterprise-grade error handling for legacy OpenMage environments:

```php
// File: app/code/community/Fballiano/ImageCleaner/controllers/Adminhtml/FbimagecleanerController.php
public function massDeleteAction()
{
    $ids = $this->getRequest()->getParam('ids');
    $db = Mage::getSingleton('core/resource')->getConnection('core_write');
    $cleaner_table = Mage::getSingleton('core/resource')->getTableName('fballiano_imagecleaner/cleaner');
    $helper = Mage::helper('fballiano_imagecleaner');
    $error_message_thrown = false;
    
    foreach ($ids as $image_id) {
        $image = $db->fetchRow("SELECT * FROM {$cleaner_table} WHERE image_id=?", $image_id);
        $image_path = $helper->getMediaDirByEntityTypeId($image['entity_type_id']) . $image["path"];
        
        // Safety check: Skip missing files and clean DB record
        if (!file_exists($image_path)) {
            $db->query("DELETE FROM {$cleaner_table} WHERE image_id=?", $image_id);
            continue;
        }
        
        // Attempt filesystem deletion
        if (unlink($image_path)) {
            $db->query("DELETE FROM {$cleaner_table} WHERE image_id=?", $image_id);
        } elseif (!$error_message_thrown) {
            $error_message_thrown = true;
            Mage::getSingleton('adminhtml/session')
                ->addError($this->__('It was not possible to delete one or more files from the filesystem.'));
        }
    }
    
    $this->_redirect('*/*');
}

```

## Practical Usage Examples

### Admin Panel Workflow

To trigger the mass delete action with missing file protection through the OpenMage backend:

1. Navigate to **System → Image Cleaner → Unused Images**
2. Select the checkbox for each image entry you wish to purge
3. Choose **Delete** from the *Actions* dropdown and click **Submit**

The POST request sends `ids` array parameters to `/admin/fballiano_imagecleaner/fbimagecleaner/massDelete`. The controller processes each ID sequentially, silently removing database entries for files that have already been deleted externally, and only attempts `unlink()` on files that actually exist.

### Programmatic Execution

You can invoke the same safety logic from custom scripts or cron jobs:

```php
<?php
require 'app/Mage.php';
Mage::app('admin');

// Simulate admin selection of image IDs
$ids = [12, 45, 78]; 
$controller = new Fballiano_ImageCleaner_Adminhtml_FbimagecleanerController();
$controller->getRequest()->setParam('ids', $ids);

// Execute mass delete with built-in missing file handling
$controller->massDeleteAction();

```

This programmatic approach benefits from identical protections: missing files are pruned from the database without throwing exceptions, while present files are physically removed and then expunged from the tracking table.

## Summary

- The mass delete action validates file existence using `file_exists()` before calling `unlink()`, preventing PHP warnings on missing files.
- Stale database records are automatically purged even when the physical file is already gone, ensuring the unused images list remains accurate.
- Error reporting is throttled to a single message per batch operation to maintain a clean admin user experience.
- The helper method `getMediaDirByEntityTypeId()` ensures correct path resolution across different entity types (products, categories, etc.).

## Frequently Asked Questions

### What happens if a file was deleted outside of OpenMage before running the mass delete action?

The controller detects the missing file via `file_exists()`, skips the `unlink()` attempt entirely, and immediately executes a SQL `DELETE` statement to remove the corresponding row from the cleaner table. This prevents PHP errors and keeps the database synchronized with the actual filesystem state.

### Does the mass delete action report all file deletion failures?

No. According to the source code in [`FbimagecleanerController.php`](https://github.com/fballiano/openmage-image-cleaner/blob/main/FbimagecleanerController.php), the system uses a `$error_message_thrown` boolean flag to display only the first deletion failure to the admin session. This design prevents the user interface from being flooded with multiple error messages when batch processing large numbers of images with permission issues or locked files.

### How does the controller determine the correct media directory for each image?

The controller utilizes `Mage::helper('fballiano_imagecleaner')->getMediaDirByEntityTypeId()`, defined in [`Helper/Data.php`](https://github.com/fballiano/openmage-image-cleaner/blob/main/Helper/Data.php), to map the `entity_type_id` stored in the database record to the proper media folder path. This abstraction handles variations between product images, category images, and other entity types without hardcoding directory paths in the controller logic.

### Can I run the mass delete action from the command line or a cron job?

Yes. You can instantiate `Fballiano_ImageCleaner_Adminhtml_FbimagecleanerController` programmatically, set the `ids` parameter on the request object, and call `massDeleteAction()`. The same safety checks apply in CLI contexts, making it safe to automate image cleanup without risking fatal errors from missing files.