protected function _deleteUnusedProductImages ($letter) { // check to see if this upgrade step has already run once // storage of this may be volatile but it's a compromise between using upgrade session (very volatile) and some new specific mysql table (permanent) $key = 'upgrade:6100:deleteUnusedProductImages:' . $letter; $keys = new Interspire_KeyStore; if ($keys->exists($key)) { return true; } // scan our product_images directory to begin detecting orphaned images $base = ISC_BASE_PATH . '/' . GetConfig('ImageDirectory') . '/' . $letter; $imageDirectoryLength = strlen(ISC_BASE_PATH . '/' . GetConfig('ImageDirectory') . '/'); $deleteCounter = 0; $directory = new RecursiveDirectoryIterator($base); foreach (new RecursiveIteratorIterator($directory) as $absoluteFilename => $current) { if ($current->isDir()) { // skip directories continue; } if (DIRECTORY_SEPARATOR == "\\") { // normalise the filename because DirectoryIterator will return \ on Windows but we store / in db $absoluteFilename = str_replace("\\", "/", $absoluteFilename); } $filename = substr($absoluteFilename, $imageDirectoryLength); // check that the filename matches our pattern of generated names such as __{random}_{size}.ext before // deleting it, if it doesn't match then it's a file that probably wasn't generated by the product.image // classes and we should leave it alone if (!preg_match('#__[0-9]{5}(_(std|thumb|tiny|zoom))?\.[^.]+$#', $filename)) { continue; } try { if ($this->_isImageInUse($filename)) { // filename exists in db somewhere; skip continue; } } catch (Exception $exception) { $this->SetError($exception->getMessage()); return false; } // remove this file if (!unlink($absoluteFilename)) { $this->SetError('unlink() failed for file: ' . $absoluteFilename); return false; } $deleteCounter++; } // the key need only exist to flag the step as run, we can store whatever we want, so let's store when the step // ran and the number of files which were deleted $keys->set($key, time() . '.' . $deleteCounter); return true; }