How OpenMage Image Cleaner Uses fnmatch for Blacklist Pattern Matching
The OpenMage Image Cleaner module uses PHP's native fnmatch() function with a */ prefix to match admin-defined glob patterns against filesystem paths, excluding protected files and folders from cleanup scans.
The fballiano/openmage-image-cleaner extension provides administrators with a flexible mechanism to safeguard specific assets from automated cleanup routines. By leveraging standard shell-style glob patterns processed through PHP's fnmatch() function, the module enables precise control over which files remain untouched during media directory scans. This implementation resides primarily within the helper class and integrates seamlessly with the recursive filesystem scanner.
Configuration and Pattern Storage
Administrators define exclusion rules through the Magento Admin interface, where patterns are stored and subsequently processed by the module's helper methods.
Admin Interface Configuration
The configuration interface is defined in app/code/community/Fballiano/ImageCleaner/etc/system.xml. The textarea field stores user input under the configuration path admin/fb_image_cleaner/blacklist as a newline-separated string. Each line represents an independent glob pattern that will be evaluated against filesystem paths during scan operations.
Retrieving Patterns in Code
The helper class converts the stored configuration into an array of patterns. In app/code/community/Fballiano/ImageCleaner/Helper/Data.php, the getBlacklistedPatterns() method retrieves and splits the configuration value:
$blacklist = Mage::getStoreConfig('admin/fb_image_cleaner/blacklist');
return preg_split('/\r\n|\r|\n/', $blacklist);
This method handles various newline formats (CRLF, CR, or LF) to ensure cross-platform compatibility when administrators paste patterns from different operating systems.
The fnmatch Implementation
The core matching logic resides in the isBlacklisted() method, which determines whether a given filesystem path matches any user-defined exclusion pattern.
The isBlacklisted Method
Located in Helper/Data.php at lines 86-92, the method iterates through all blacklist patterns and applies fnmatch() to each candidate path:
public function isBlacklisted($path, $blacklisted_patterns): bool
{
foreach ($blacklisted_patterns as $blacklisted_pattern) {
if (fnmatch('*/' . $blacklisted_pattern, $path)) return true;
}
return false;
}
The function prepends */ to every admin-defined pattern before passing it to fnmatch(). This transformation ensures that patterns match regardless of the absolute path prefix preceding the media directory.
The Critical */ Prefix
The prepended */ wildcard serves a specific architectural purpose. The scanner operates on full filesystem paths (e.g., /var/www/html/media/wysiwyg/banner.jpg), while administrators define patterns relative to the media root (e.g., wysiwyg/banner.jpg).
By prefixing patterns with */, the code forces the match to occur at any directory depth:
- Without prefix:
fnmatch('wysiwyg/*.jpg', '/var/www/media/wysiwyg/banner.jpg')returnsfalse - With prefix:
fnmatch('*/wysiwyg/*.jpg', '/var/www/media/wysiwyg/banner.jpg')returnstrue
The leading asterisk consumes the server-specific path components up to the media directory, allowing portable patterns that work across different server configurations.
Integration with the File Scanner
The blacklist integrates directly into the recursive directory traversal, filtering protected assets before they reach the cleanup logic.
Recursive Directory Scanning
The scandirRecursive() method in Helper/Data.php (lines 15-32) invokes isBlacklisted() for every file and folder encountered:
public function scandirRecursive($dir)
{
$result = [];
$blacklist_patterns = $this->getBlacklistedPatterns();
$root = scandir($dir);
foreach ($root as $value) {
if ($value === '.' || $value === '..') continue;
$fullPath = "$dir/$value";
// Skip anything matching the blacklist
if ($this->isBlacklisted($fullPath, $blacklist_patterns)) {
continue;
}
if (is_file($fullPath)) {
$result[] = $fullPath;
} else {
$result = array_merge($result, $this->scandirRecursive($fullPath));
}
}
return $result;
}
When isBlacklisted() returns true, the scanner immediately skips that entry via continue, preventing blacklisted items from appearing in the cleanup candidates list.
Protected Asset Categories
This mechanism effectively protects:
- Individual files: Specific design assets like
logo.pngorfavicon.ico - Directory trees: Entire folders such as
wysiwyg/legacy/orcatalog/custom/ - Pattern-based groups: File collections matching
*.pdforlanding202*/*
The controller layer (FbimagecleanerController.php) relies entirely on this helper implementation and does not perform additional blacklist validation, ensuring consistent behavior across all cleanup operations.
Supported Pattern Syntax
Because the module delegates to PHP's native fnmatch() without the FNM_PATHNAME flag, administrators can utilize standard shell glob syntax:
*: Matches zero or more characters (including directory separators when used with the*/prefix)?: Matches exactly one character[abc]: Matches any single character from the specified set[a-z]: Matches any single character within the range{a,b}: Brace expansion (when supported by the underlying system)
Patterns operate on the full relative path from the media root. For example, catalog/product/cache/* excludes all cached product images while preserving original uploads.
Summary
- Pattern Storage: Administrators enter newline-separated glob patterns in Stores → Configuration → Advanced → Image Cleaner, stored under
admin/fb_image_cleaner/blacklist. - Pattern Retrieval: The
getBlacklistedPatterns()method inHelper/Data.phpsplits the configuration into an array usingpreg_split()to handle multiple newline formats. - Matching Logic: The
isBlacklisted()method prepends*/to each pattern and appliesfnmatch()to test against full filesystem paths. - Scanner Integration: The recursive scanner calls
isBlacklisted()for every entry, skipping matches to prevent protected files from being processed for deletion. - Syntax Support: Standard shell glob patterns (
*,?,[...]) are fully supported, enabling flexible exclusion rules without regex complexity.
Frequently Asked Questions
How do I add multiple blacklist patterns in the OpenMage Image Cleaner?
Enter each pattern on a separate line in the blacklist textarea located at Stores → Configuration → Advanced → Image Cleaner. The getBlacklistedPatterns() method automatically splits the input using preg_split('/\r\n|\r|\n/', ...) to handle Windows, Mac, and Unix line endings, returning an array of individual patterns for processing.
Why does the module prepend */ to my blacklist patterns?
The */ prefix ensures that patterns match regardless of the server's absolute filesystem path preceding the media directory. Since fnmatch() performs literal string matching, without this prefix, a pattern like wysiwyg/*.jpg would fail to match /var/www/html/media/wysiwyg/photo.jpg. The leading wildcard consumes the variable server path components, allowing portable patterns that work across different hosting environments.
Can I use regular expressions instead of glob patterns for blacklisting?
No, the module exclusively uses fnmatch() for pattern matching, which supports shell-style glob syntax only. Regular expressions are not supported in the blacklist configuration. To exclude specific files, use glob patterns such as catalog/product/banner.jpg or wysiwyg/important*/*.png instead of regex syntax.
Does the blacklist apply to all cleanup operations in the module?
Yes, the blacklist applies universally to all file scans performed through the scandirRecursive() method in Helper/Data.php. Since the admin controller relies entirely on this helper method for filesystem enumeration, any path matching a blacklist pattern is excluded from the candidate list before deletion options are presented, ensuring consistent protection across the entire cleanup workflow.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →