HEX
Server: Apache
System: Linux outside 5.4.8_Algonet_ZeroMAC_0.9.4.8 #6 SMP Mon Feb 3 20:36:07 CET 2020 x86_64
User: server (1002)
PHP: 8.3.20
Disabled: exec,passthru,shell_exec,system,proc_open,popen,curl_multi_exec,parse_ini_file,show_source
Upload Files
File: /home/muoapartman/www/wp-content/plugins/shortpixel-image-optimiser/class/shortpixel-png2jpg.php
<?php
/**
 * User: simon
 * Date: 17.11.2017
 * Time: 13:44
 */

 use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
 use ShortPixel\Model\FileModel as FileModel;
 use ShortPixel\Model\Directorymodel as DirectoryModel;

//TODO decouple from directly using WP metadata, in order to be able to use it for custom images
class ShortPixelPng2Jpg {
    private $_settings = null;

    public function __construct($settings){
        if(function_exists('wp_raise_memory_limit')) {
            wp_raise_memory_limit( 'image' );
        }
        $this->_settings = $settings;
    }

    protected function canConvertPng2Jpg($image) {
        $transparent = 0;
        $transparent_pixel = $img = $bg = false;

        //WPShortPixel::log("PNG2JPG SHELL EXEC: " . shell_exec('convert ' . $image . ' -format "%[opaque]" info:'));

        if (!file_exists($image)) {
            Log::addDebug("PNG2JPG FILE MISSING:  " . $image);
            $transparent = 1;
        } elseif(ord(file_get_contents($image, false, null, 25, 1)) & 4) {
            Log::addDebug("PNG2JPG: 25th byte has thrid bit 1 - transparency");
            $transparent = 1;
        } else {
            $contents = file_get_contents($image);
            if (stripos($contents, 'PLTE') !== false && stripos($contents, 'tRNS') !== false) {
                $transparent = 1;
            }
            if (!$transparent) {
                $is = getimagesize($image);
                Log::addDebug("PNG2JPG Image width: " . $is[0] . " height: " . $is[1] . " aprox. size: " . round($is[0]*$is[1]*5/1024/1024) . "M memory limit: " . ini_get('memory_limit') . " USED: " . memory_get_usage());
                Log::addDebug("PNG2JPG create from png $image");
                $img = @imagecreatefrompng($image);
                Log::addDebug("PNG2JPG created from png");
                if(!$img) {
                    Log::addDebug("PNG2JPG not a PNG, imagecreatefrompng failed ");
                    $transparent = true; //it's not a PNG, can't convert it
                } else {
                    Log::addDebug("PNG2JPG is PNG");
                    $w = imagesx($img); // Get the width of the image
                    $h = imagesy($img); // Get the height of the image
                    Log::addDebug("PNG2JPG width $w height $h. Now checking pixels.");
                    //run through pixels until transparent pixel is found:
                    for ($i = 0; $i < $w; $i++) {
                        for ($j = 0; $j < $h; $j++) {
                            $rgba = imagecolorat($img, $i, $j);
                            if (($rgba & 0x7F000000) >> 24) {
                                $transparent_pixel = true;
                                break;
                            }
                        }
                    }
                }
            }
        } // non-transparant.

        Log::addDebug("PNG2JPG is " . (!$transparent && !$transparent_pixel ? " not" : "") . " transparent");
        //pass on the img too, if it was already loaded from PNG, matter of performance
        return array('notTransparent' => !$transparent && !$transparent_pixel, 'img' => $img);
    }

    /**
     *
     * @param array $params
     * @param string $backupPath
     * @param string $suffixRegex for example [0-9]+x[0-9]+ - a thumbnail suffix - to add the counter of file name collisions files before that suffix (img-2-150x150.jpg).
     * @param image $img - the image if it was already created from png. It will be destroyed at the end.
     * @return string
     */

    protected function doConvertPng2Jpg($params, $backup, $suffixRegex = false, $img = false) {
        $image = $params['file'];
        $fs = \wpSPIO()->filesystem();

        Log::addDebug("PNG2JPG doConvert $image");
        if(!$img) {
            Log::addDebug("PNG2JPG doConvert create from PNG");
            $img = (file_exists($image) ? imagecreatefrompng($image) : false);
            if(!$img) {
                Log::addDebug("PNG2JPG doConvert image cannot be created.");
                return (object)array("params" => $params, "unlink" => false); //actually not a PNG.
            }
        }

      //  WPShortPixel::log("PNG2JPG doConvert img ready");
        $x = imagesx($img);
        $y = imagesy($img);
        Log::addDebug("PNG2JPG doConvert width $x height $y");
        $bg = imagecreatetruecolor($x, $y);
    //    WPShortPixel::log("PNG2JPG doConvert img created truecolor");
        if(!$bg) return (object)array("params" => $params, "unlink" => false);
        imagefill($bg, 0, 0, imagecolorallocate($bg, 255, 255, 255));
        imagealphablending($bg, 1);
        imagecopy($bg, $img, 0, 0, 0, 0, $x, $y);
        imagedestroy($img);
        //$newPath = preg_replace("/\.png$/i", ".jpg", $image);

        $fsFile = $fs->getFile($image); // the original png file
        $filename = $fsFile->getFileName();
        $newFileName = $fsFile->getFileBase() . '.jpg'; // convert extension to .png

        $fsNewFile = $fs->getFile($fsFile->getFileDir() . $newFileName);

        $uniquefile = $this->unique_file( $fsFile->getFileDir(), $fsNewFile);
        $newPath =  $uniquefile->getFullPath(); //(string) $fsFile->getFileDir() . $uniquepath;

        // check old filename, replace with uniqued filename.
        $newUrl = str_replace($filename, $uniquefile->getFileName(), $params['url']); //preg_replace("/\.png$/i", ".jpg", $params['url']);

        if (imagejpeg($bg, $newPath, 90)) {
            Log::addDebug("PNG2JPG doConvert created JPEG at $newPath");
            $newSize = filesize($newPath);
            $origSize = filesize($image);
            if($newSize > $origSize * 0.95 || $newSize == 0) {
                //if the image is not 5% smaller, don't bother.
                //if the size is 0, a conversion (or disk write) problem happened, go on with the PNG
                Log::addDebug("PNG2JPG converted image is larger ($newSize vs. $origSize), keeping the PNG");
                unlink($newPath);
                return (object)array("params" => $params, "unlink" => false);
            }
            //backup?
            if($backup) {
                $imageForBk = trailingslashit(dirname($image)) . ShortPixelAPI::MB_basename($newPath, '.jpg') . '.png';
                Log::addDebug("imageForBk should be PNG: $imageForBk");
                if($image != $imageForBk) {
                    Log::addDebug("PNG2JPG doConvert rename $image to $imageForBk");
                    @rename($image, $imageForBk);
                }
                if(!file_exists($imageForBk)) {
                    unlink($newPath);
                    return (object)array("params" => $params, "unlink" => false);
                }
                $image = $imageForBk;
                $ret = ShortPixelAPI::backupImage($image, array($image));
                if($ret['Status'] !== ShortPixelAPI::STATUS_SUCCESS) {
                    Log::addDebug("PNG2JPG couldn't backup, keeping the PNG");
                    unlink($newPath);
                    return (object)array("params" => $params, "unlink" => false);
                }
            }
            //unlink($image);
            $params['file'] = $newPath;
            Log::addDebug("Original_file should be PNG: $image");
            $params['original_file'] = $image;
            $params['url'] = $newUrl;
            $params['type'] = 'image/jpeg';
            $params['png_size'] = $origSize;
            $params['jpg_size'] = $newSize;
        }
        return (object)array("params" => $params, "unlink" => $image);
    }

    /** Own function to get a unique filename since the WordPress wp_unique_filename seems to not function properly w/ thumbnails */
    private function unique_file(DirectoryModel $dir, FileModel $file, $number = 0)
    {
      if (! $file->exists())
        return $file;

      $number = 0;
      $fs = \wpSPIO()->filesystem();

      $base = $file->getFileBase();
      $ext = $file->getExtension();

      while($file->exists())
      {
        $number++;
        $numberbase = $base . '-' . $number;
        Log::addDebug('check for unique file -- ' . $dir->getPath() . $numberbase . '.' . $ext);
        $file = $fs->getFile($dir->getPath() . $numberbase . '.' . $ext);
      }

      return $file;

    }

    protected function isExcluded($params) {
        if(is_array($this->_settings->excludePatterns)) {
            foreach($this->_settings->excludePatterns as $item) {
                $type = trim($item["type"]);
                if(in_array($type, array('name', 'path')) && WpShortPixel::matchExcludePattern($params['file'], $item['value'])) {
                    return true; //excluded by name pattern
                }
                if(isset($params['width']) && isset($params['height']) && 'size' == $type && WPShortPixel::isProcessableSize($params['width'], $params['height'], $item['value'])){
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Convert an uploaded image from PNG to JPG
     * @param type $params ( file, url, type )  - Connected to https://developer.wordpress.org/reference/hooks/wp_handle_upload/
     * @return string
     */
    public function convertPng2Jpg($params) {

        //echo("PARAMS : ");var_dump($params);
        if(!$this->_settings->png2jpg || strtolower(substr($params['file'], -4)) !== '.png') {
            return $params;
        }
        if($this->isExcluded($params)) { return $params; }

        $image = $params['file'];
        Log::addDebug("Convert Media PNG to JPG on upload: {$image}");

        if($this->_settings->png2jpg == 2) {
            $doConvert = true;
        } else {
            $ret = $this->canConvertPng2Jpg($image);
            $doConvert =  $ret['notTransparent'];
        }
        if ($doConvert) {
            $ret = $this->doConvertPng2Jpg($params, $this->_settings->backupImages, false, isset($ret['img']) ? $ret['img'] : false);
            if($ret->unlink) @unlink($ret->unlink);
            $paramsC = $ret->params;
            if($paramsC['type'] == 'image/jpeg') {
                // we don't have metadata, so save the information in a temporary map
                $conv = $this->_settings->convertedPng2Jpg;
                //do a cleanup first
                foreach($conv as $key => $val) {
                    if(time() - $val['timestamp'] > 3600) unset($conv[$key]);
                }
                $conv[$paramsC['file']] = array('pngFile' => $paramsC['original_file'], 'backup' => $this->_settings->backupImages,
                    'optimizationPercent' => round(100.0 * (1.00 - $paramsC['jpg_size'] / $paramsC['png_size'])),
                    'timestamp' => time());
                $this->_settings->convertedPng2Jpg = $conv;
            }
            return $paramsC;
        }
        return $params;
    }

    /**
     * convert PNG to JPEG if possible - already existing image in Media Library
     *
     * @param type $meta
     * @param type $ID
     * @return string
     */
    public function checkConvertMediaPng2Jpg($itemHandler) {

        $meta = $itemHandler->getRawMeta();
        $ID = $itemHandler->getId();
        $fs = \wpSPIO()->filesystem();

        if(!$this->_settings->png2jpg || !isset($meta['file']) || strtolower(substr($meta['file'], -4)) !== '.png') {
            return ;
        }
        if($this->isExcluded($meta)) { return; }

        Log::addDebug("Send to processing: Convert Media PNG to JPG #{$ID} META: " . json_encode($meta));

        $image = $meta['file']; // This is not a full path!
        $imageFile = $fs->getAttachedFile($ID);
        $imagePath = $imageFile->getFullPath(); // This is a full path.
        $basePath = trailingslashit(str_replace($image, "", $imagePath));
        $imageUrl = wp_get_attachment_url($ID);
        $baseUrl = self::removeUrlProtocol(trailingslashit(str_replace($image, "", $imageUrl))); //make the base url protocol agnostic if it's not already

        // set a temporary error in order to make sure user gets something if the image failed from memory limit.
        if(   isset($meta['ShortPixel']['Retries']) && $meta['ShortPixel']['Retries'] > 3
           && isset($meta['ShortPixel']['ErrCode']) && $meta['ShortPixel']['ErrCode'] == ShortPixelAPI::ERR_PNG2JPG_MEMORY) {
            Log::addWarn("PNG2JPG too many memory failures!");
            throw new Exception('Not enough memory to convert from PNG to JPG.', ShortPixelAPI::ERR_PNG2JPG_MEMORY);
        }
        $meta['ShortPixelImprovement'] = 'Error: <i>Not enough memory to convert from PNG to JPG.</i>';
        if(!isset($meta['ShortPixel']) || !is_array($meta['ShortPixel'])) {
            $meta['ShortPixel'] = array();
        }
        $meta['ShortPixel']['Retries'] = isset($meta['ShortPixel']['Retries']) ? $meta['ShortPixel']['Retries'] + 1 : 1;
        $meta['ShortPixel']['ErrCode'] = ShortPixelAPI::ERR_PNG2JPG_MEMORY;
        //wp_update_attachment_metadata($ID, $meta);

        update_post_meta($ID, '_wp_attachment_metadata', $meta);

        if($this->_settings->png2jpg == 2) {
            $doConvert = true;
        } else {
            $retC = $this->canConvertPng2Jpg($imagePath);

            $doConvert =  $retC['notTransparent'];
        }
        if (!$doConvert) {
            Log::addDebug("PNG2JPG not a PNG, or transparent when this setting is off - " . $imagePath);
            return $meta; //cannot convert it
        }

        Log::addDebug(" CONVERTING MAIN: $imagePath");
        $retMain = $this->doConvertPng2Jpg(array('file' => $imagePath, 'url' => false, 'type' => 'image/png'), $this->_settings->backupImages, false, isset($retC['img']) ? $retC['img'] : false);
        Log::addDebug("PNG2JPG doConvert Main RETURNED " . json_encode($retMain));
        $ret = $retMain->params;
        $toUnlink = array();
        $toReplace = array();

        //unset the temporary error
        unset($meta['ShortPixelImprovement']);
        unset($meta['ShortPixel']['ErrCode']);
        $meta['ShortPixel']['Retries'] -= 1;
        //wp_update_attachment_metadata($ID, $meta);
        update_post_meta($ID, '_wp_attachment_metadata', $meta);

        if ($ret['type'] == 'image/jpeg') {
            $toUnlink[] = $retMain->unlink;
            do_action('shortpixel/image/convertpng2jpg_before', $ID, $meta);
            //convert to the new URLs the urls in the existing posts.
            $baseRelPath = dirname($image);
            // this happens when image does not have a subdir structure like 2021/07
			      if ($baseRelPath == '.')
				     $baseRelPath = '';
			         else
				    $baseRelPath = trailingslashit($baseRelPath);

            $toReplace[self::removeUrlProtocol($imageUrl)] = $baseUrl . $baseRelPath . wp_basename($ret['file']);
            $pngSize = $ret['png_size'];
            $jpgSize = $ret['jpg_size'];
            Log::addDebug(" IMAGE PATH: $imagePath");
            $imagePath = isset($ret['original_file']) ? $ret['original_file'] : $imagePath;
            Log::addDebug(" SET IMAGE PATH: $imagePath");

            //conversion succeeded for the main image, update meta and proceed to thumbs. (It could also not succeed if the converted file is not smaller)
            $duplicates = $this->updateFileAlsoInWPMLDuplicates($ID, $meta, str_replace($basePath, '', $ret['file']));
            Log::addDebug(" WPML duplicates: " . json_encode($duplicates));

            $originalSizes = isset($meta['sizes']) ? $meta['sizes'] : array();
            $filesConverted = array();
            foreach($meta['sizes'] as $size => $info) {
                if(isset($filesConverted[$info['file']])) {
                    Log::addDebug("PNG2JPG DUPLICATED THUMB: " . $size);
                    if($filesConverted[$info['file']] === false) {
                        Log::addDebug("PNG2JPG DUPLICATED THUMB not converted");
                        continue;
                    }
                    Log::addDebug("PNG2JPG DUPLICATED THUMB already converted");
                    $rett = $filesConverted[$info['file']];
                } else {
                    $retThumb = $this->doConvertPng2Jpg(array('file' => $basePath . $baseRelPath . $info['file'], 'url' => false, 'type' => 'image/png'),
                        $this->_settings->backupImages, "[0-9]+x[0-9]+");
                    $rett = $retThumb->params;
                }

                Log::addDebug("PNG2JPG doConvert thumb RETURNED " . json_encode($rett));
                if ($rett['type'] == 'image/jpeg') {
                    $toUnlink[] = $retThumb->unlink;
                    Log::addDebug("PNG2JPG thumb is jpg");
                    $pngSize += $rett['png_size'];
                    $jpgSize += $rett['jpg_size'];
                    Log::addDebug("PNG2JPG total PNG size: $pngSize total JPG size: $jpgSize");
                    $originalSizes[$size]['file'] = wp_basename($rett['file'], '.jpg') . '.png';
                    Log::addDebug("PNG2JPG thumb original: " . $originalSizes[$size]['file']);
                    $toReplace[$baseUrl . $baseRelPath . $info['file']] = $baseUrl . $baseRelPath . wp_basename($rett['file']);

                    $filesConverted[$info['file']] = $rett;
                    $this->updateThumbAlsoInWPMLDuplicates($ID, $meta, $duplicates, $size, wp_basename($rett['file']));
                } else {
                    $filesConverted[$info['file']] = false;
                }
            }
            $meta['ShortPixelPng2Jpg'] = array('originalFile' => $imagePath, 'originalSizes' => $originalSizes,
                'backup' => $this->_settings->backupImages,
                'optimizationPercent' => round(100.0 * (1.00 - $jpgSize / $pngSize)));
            //wp_update_attachment_metadata($ID, $meta);
            update_post_meta($ID, '_wp_attachment_metadata', $meta);
            $itemHandler->deleteItemCache(); // remove cache since filetype changes.
            Log::addDebug("Updated meta: " . json_encode($meta));
						wp_update_post(array('ID' => $ID, 'post_mime_type' => 'image/jpeg' ));
            do_action('shortpixel/image/convertpng2jpg_after', $ID, $meta);
        }

        if(count($toReplace)) {
            self::png2JpgUpdateUrls(array(), $toReplace);
        }
        $fs = \wpSPIO()->filesystem();

        foreach($toUnlink as $unlink) {
            if($unlink) {
                Log::addDebug("PNG2JPG remove file $unlink");
                $fileObj = $fs->getFile($unlink);
                $fileObj->delete();
//                @unlink($unlink);
            }
        }
        Log::addDebug("PNG2JPG done. Return: " . json_encode($meta));

        return $meta;
    }

    /**
     * @param $parentID
     * @param $parentMeta by ref. is changed
     * @param $file
     * @return array WPML duplicates
     */
    protected function updateFileAlsoInWPMLDuplicates($parentID, &$parentMeta, $file){
        $duplicates = ShortPixelMetaFacade::getWPMLDuplicates($parentID);
        foreach($duplicates as $ID) {
            $meta = $parentID == $ID ? $parentMeta : wp_get_attachment_metadata($ID);
            $meta['file'] = $file;
            $meta['type'] = 'image/jpeg';
            if($parentID == $ID) $parentMeta = $meta;
            update_attached_file($ID, $meta['file']);
            //wp_update_attachment_metadata($ID, $meta);
            update_post_meta($ID, '_wp_attachment_metadata', $meta);
        }
        return $duplicates;
    }

    /**
     * @param $parentID
     * @param $parentMeta by ref. is changed
     * @param $duplicates
     * @param $size
     * @param $thumbnail
     */
    protected function updateThumbAlsoInWPMLDuplicates($parentID, &$parentMeta, $duplicates, $size, $thumbnail) {
        foreach($duplicates as $ID) {
            $meta = $parentID == $ID ? $parentMeta : wp_get_attachment_metadata($ID);
            $meta['sizes'][$size]['file'] = wp_basename($thumbnail);
            $meta['sizes'][$size]['mime-type'] = 'image/jpeg';
            if($parentID == $ID) $parentMeta = $meta;
            //wp_update_attachment_metadata($ID, $meta);
            update_post_meta($ID, '_wp_attachment_metadata', $meta);
        }
    }

    /**
     * taken from Velvet Blues Update URLs plugin
     * @param $options
     * @param $oldurl
     * @param $newurl
     * @return array
     */
    public static function png2JpgUpdateUrls($options, $map){
        global $wpdb;
        Log::addDebug("PNG2JPG update URLS " . json_encode($map));
        $results = array();
        $queries = array(
            'content' =>		array("UPDATE $wpdb->posts SET post_content = replace(post_content, %s, %s)",  __('Content Items (Posts, Pages, Custom Post Types, Revisions)','shortpixel-image-optimiser') ),
            'excerpts' =>		array("UPDATE $wpdb->posts SET post_excerpt = replace(post_excerpt, %s, %s)", __('Excerpts','shortpixel-image-optimiser') ),
            'attachments' =>	array("UPDATE $wpdb->posts SET guid = replace(guid, %s, %s) WHERE post_type = 'attachment'",  __('Attachments','shortpixel-image-optimiser') ),
            'links' =>			array("UPDATE $wpdb->links SET link_url = replace(link_url, %s, %s)", __('Links','shortpixel-image-optimiser') ),
            'custom' =>			array("UPDATE $wpdb->postmeta SET meta_value = replace(meta_value, %s, %s)",  __('Custom Fields','shortpixel-image-optimiser') ),
            'guids' =>			array("UPDATE $wpdb->posts SET guid = replace(guid, %s, %s) ",  __('GUIDs','shortpixel-image-optimiser') )
        );
        if(count($options) == 0) {
            $options = array_keys($queries);
        }
        $startTime = microtime(true);
        foreach($options as $option){
          //  WPShortPixel::log("PNG2JPG update URLS on $option ");
            if( $option == 'custom' ){
                $n = 0;
                $page_size = WpShortPixelMediaLbraryAdapter::getOptimalChunkSize('postmeta');

                for( $page = 0; $items = $wpdb->get_results("SELECT * FROM $wpdb->postmeta LIMIT " . ($page * $page_size) . ", $page_size"); $page++ ) {
                    foreach( $items as $item ) {
                        $value = $item->meta_value;
                        if( trim($value) == '' || $item->meta_key == '_wp_attached_file' || $item->meta_key == '_wp_attachment_metadata') {
                            continue;
                        }

                        $edited = (object)array('data' => $value, 'replaced' => false);
                        foreach($map as $oldurl => $newurl) {
                            if (strlen($newurl)) {
                                $editedOne = self::png2JpgUnserializeReplace($oldurl, $newurl, $edited->data);
                                $edited->data = $editedOne->data;
                                $edited->replaced = $edited->replaced || $editedOne->replaced;
                            }
                        }
                        if( $edited->replaced ){
                            $fix = $wpdb->query("UPDATE $wpdb->postmeta SET meta_value = '".$edited->data."' WHERE meta_id = ".$item->meta_id );
                            if( $fix )
                                $n++;
                        }
                    }
                    //check time. This loop could take long because it's scanning all the postmeta table which in some cases becomes huge...
                    $timeElapsed = microtime(true) - $startTime;
                    if($timeElapsed > SHORTPIXEL_MAX_EXECUTION_TIME / 2) {
                        //try to add some time or get out if not
                        if(\wpSPIO()->env()->is_function_usable('set_time_limit') && set_time_limit(SHORTPIXEL_MAX_EXECUTION_TIME)) {
                            $startTime += SHORTPIXEL_MAX_EXECUTION_TIME / 2;
                        } else {
                            break;
                        }
                    }
                }
                $results[$option] = array($n, $queries[$option][1]);
            }
            else {
                foreach($map as $oldurl => $newurl) {
                    if(strlen($newurl)) {
												$prepared = $wpdb->prepare( $queries[$option][0], $oldurl, $newurl);
												Log::addDebug("Prepared Query", $prepared);
                        $result = $wpdb->query( $prepared );
                        $results[$option] = array($result, $queries[$option][1]);
                    }
                }
            }
        }
        return $results;
    }

    public static function removeUrlProtocol($url) {
        return preg_replace("/^http[s]{0,1}:\/\//", "", $url);
    }

    /**
     * taken from Velvet Blues Update URLs plugin
     * @param string $from
     * @param string $to
     * @param string $data
     * @param bool|false $serialised
     * @return array|mixed|string
     */
    public static function png2JpgUnserializeReplace( $from = '', $to = '', $data = '', $serialised = false ) {
        $replaced = false;
        try {
            if ( false !== is_serialized( $data ) ) {

                if(false === strpos($data, wp_basename($from))) {
                    return (object)array('data' => $data, 'replaced' => false); //quick pre-screening
                }

                $unserialized = unserialize( $data );
                $ret = self::png2JpgUnserializeReplace( $from, $to, $unserialized, true );
                $data = $ret->data;
                $replaced = $replaced || $ret->replaced;
            }
            elseif ( is_array( $data ) ) {
                $_tmp = array( );
                foreach ( $data as $key => $value ) {
                    $ret = self::png2JpgUnserializeReplace( $from, $to, $value, false );
                    $_tmp[ $key ] = $ret->data;
                    $replaced = $replaced || $ret->replaced;
                }
                $data = $_tmp;
                unset( $_tmp );
            }
            elseif(is_object( $data )) {
                foreach(get_object_vars($data) as $key => $value) {
                    $ret = self::png2JpgUnserializeReplace( $from, $to, $value, false );
                    $_tmp[ $key ] = $ret->data;
                    $replaced = $replaced || $ret->replaced;
                }
                $data = (object)$_tmp;
            }
            elseif ( is_string( $data )) {
                if(false !== strpos($data, $from)) {
                    $replaced = true;
                    $data = str_replace( $from, $to, $data );
                } elseif(   strlen($from) > strlen($data) //data is shorter than the url to be replaced - could be a relative path?
                         && strlen($data) >= strlen(wp_basename($from)) //but should at least contain the file name
                         && strpos($from, $data) == strlen($from) - strlen($data)) {
                    $replaced = true;
                    $data = substr($to, strlen($from) - strlen($data));
                }
            }
            if ( $serialised ) {
                return (object)array('data' => serialize($data), 'replaced' => $replaced);
            }
        } catch( Exception $error ) {
        }
        return (object)array('data' => $data, 'replaced' => $replaced);
    }
}