4.1(WPチケット#18532)での画像のスケーリング(丸め)の変更に関する問題の処理


17

現在、サイトコンテンツを古い4.1より前のサイトから新しいセットアップに移行し、#18532の丸めエラーの問題とそれに対応する修正を行っています

これを要約すると、WordPress側での長年にわたる丸めの誤動作が修正されました。

693x173で画像をアップロードし、幅300に拡大縮小したとします。

  • pre 4.1:300x74
  • 4.1以降:300x75

問題

一般に、これは既存のファイルに影響を与えないため、問題を引き起こしません<img>

ただし、サムを再生成するか、WXRファイルから添付ファイルをインポートすると、ファイルシステムで生成が異なり、すべて<img>post_content死んでしまいます。

解決策を探しています

私はさまざまな解決策を考えてきました:

悪い昔に戻る

チェンジセット30660は新しいフィルターを導入しましたwp_constrain_dimensions 4.1より前の古い動作をプラグインするために使用できるがました。これにより、問題が修正されます。しかし、これが後で問題を引き起こす可能性があるのか​​疑問に思っており、一般的には修正したいので、これはうまくいきますが、理想的ではないと思います。

タイムズゼアアーアーチャンギン

したがって、これには別の目標があります。DBをクリーンアップし、古いファイルへのすべての参照を新しいファイルへの参照に置き換えます。ここで実際に質問しているのは、これを行う方法です。この問題は多くの人々に影響を及ぼし、多くの人々に影響を与えると思われるため、効果的で一般的に適用可能なソリューションを探しています

私の現在のアイデアはこれです:

  1. インポート、再生成、または新しいファイルと壊れたタグを残すものは何でも。
  2. ファイルシステム内のすべてのサイズ変更されたファイルからリストAを作成するか、データベースからこの情報を取得します
  3. このリストを解析し、4.1より前に見えるように、ファイル名がすべて1ピクセルオフセットされた2番目のリストBを作成します
  4. Bのすべての出現をAの関連エントリに置き換えて、データベース全体を検索および置換します。

これがこの状況を処理する最もスマートで効率的な方法であるかどうかはわかりません。また、少し強引すぎる感じもします。実装する前に、WPSEクラウドの無限の知恵で確認したかっただけです;)

[編集]読んだCK-マクラウドの答えを(ありがとう!)私はあなたが常にあなたの頭の後ろでこの問題を維持する必要はありませんので、修正はきっぱりとこの問題を解決すべきだと思います。[/編集]

[編集2] Tracで関連チケットを見つけました。参照用に追加しています。[/ edit2]


どこerror issue of #13852でした#18532か?:)
Aravona

2
おっと、はい、修正されました。:)
クラフトナー

回答:


4

これは、インポーターでコンテンツをインポートするときに機能し、URLを完全に修正する他の回答とは別のアプローチです。繰り返しますが、これはバトルテストではありませんが、私が決めた解決策であり、うまくいきました。

私はこれを好むが、それは問題を完全に解決し、それがうまくいけばうまくいくからだ。壊れたものをDBに残さずに表示して修正するので、後で壊れる心配はありません。

/*
Plugin Name:  Bugfix Ticket 31581 for WP_Importer
Plugin URI:   /wordpress//a/206992/47733
Description:  Fixes image references after post WP 4.1 image scaling change in post content when using the WP_Importer  (see https://core.trac.wordpress.org/ticket/31581)
Version:      0.0.1
*/

class Bugfix31581WPImporter {

    protected $remaps;

    /**
     * Initialize class, mainly setting up hooks
     */
    public function init(){

        if (WP_IMPORTING === true){

            $this->remaps = array();

            //This hook is chosen because it is pretty close to where the actual attachment import is happening.
            //TODO: May be reconsidered.
            add_filter('wp_update_attachment_metadata', array($this, 'collectRemaps'), 10 , 2);

            add_action('import_end', array($this, 'remap'));
            add_action('import_end', array($this, 'importEnded'));
        }

    }

    /**
     * Cleans up hooks after the import has ended.
     */
    public function importEnded(){
        remove_filter('wp_update_attachment_metadata', array($this, 'collectRemaps'), 10);

        remove_action('import_end', array($this, 'remap'), 10);
        remove_action('import_end', array($this, 'importEnded'), 10);
    }

    /**
     * When an attachment is added compare the resulting sizes with the sizes from the legacy algorithm and setup remap.
     *
     * @param $data
     * @param $post_id
     *
     * @return mixed
     */
    public function collectRemaps($data, $post_id ){

        $intermediate_sizes = $this->getIntermediateSizes();

        if(empty($data) || !array_key_exists('sizes', $data)){
            return $data;
        }

        foreach($data['sizes'] as $key => $size){

            $size_new_algorithm = array($size['width'], $size['height']);

            $dest_w = $intermediate_sizes[$key]['width'];
            $dest_h = $intermediate_sizes[$key]['height'];
            $crop = $intermediate_sizes[$key]['crop'];

            add_filter('wp_constrain_dimensions', array($this, 'legacy_wp_constrain_dimensions'), 10, 5);

            $size_old_algorithm = image_resize_dimensions($data['width'], $data['height'], $dest_w, $dest_h, $crop);

            //Bail out in the rare case of `image_resize_dimensions` returning false
            if($size_old_algorithm===false){
                continue;
            }

            $size_old_algorithm = array($size_old_algorithm[4], $size_old_algorithm[5]);

            remove_filter('wp_constrain_dimensions', array($this, 'legacy_wp_constrain_dimensions'), 10);

            // Compare the current size with the calculation of the old algorithm...
            $diff = array_diff($size_new_algorithm, $size_old_algorithm);

            // ...to detect any mismatches
            if(!empty($diff)){

                $oldFilename = $this->getOldFilename($size['file'], $size_old_algorithm);

                // If getting the old filename didn't work for some reason (probably other filename-structure) bail out.
                if($oldFilename===false){
                    continue;
                }

                if(!array_key_exists($post_id, $this->remaps)){
                    $this->remaps[$post_id] = array();
                }

                $this->remaps[$post_id][$size['file']] = array(
                    'file' => $oldFilename,
                    'size' => $key
                );
            }

        }

        return $data;
    }

    /**
     * Get resize settings for all image sizes
     *
     * Taken from wp_generate_attachment_metadata() in includes/image.php
     *
     * @return array
     */
    public function getIntermediateSizes(){

        global $_wp_additional_image_sizes;

        $sizes = array();
        foreach ( get_intermediate_image_sizes() as $s ) {
            $sizes[$s] = array( 'width' => '', 'height' => '', 'crop' => false );
            if ( isset( $_wp_additional_image_sizes[$s]['width'] ) )
                $sizes[$s]['width'] = intval( $_wp_additional_image_sizes[$s]['width'] ); // For theme-added sizes
            else
                $sizes[$s]['width'] = get_option( "{$s}_size_w" ); // For default sizes set in options
            if ( isset( $_wp_additional_image_sizes[$s]['height'] ) )
                $sizes[$s]['height'] = intval( $_wp_additional_image_sizes[$s]['height'] ); // For theme-added sizes
            else
                $sizes[$s]['height'] = get_option( "{$s}_size_h" ); // For default sizes set in options
            if ( isset( $_wp_additional_image_sizes[$s]['crop'] ) )
                $sizes[$s]['crop'] = $_wp_additional_image_sizes[$s]['crop']; // For theme-added sizes
            else
                $sizes[$s]['crop'] = get_option( "{$s}_crop" ); // For default sizes set in options
        }

        return $sizes;
    }

    /**
     * Turn the new filename into the old filename reducing the height by one
     *
     * @param $newFilename
     * @param $size
     *
     * @return mixed
     */
    public function getOldFilename($newFilename, $size){

        $dimensions = array();

        $filetypes = $this->getAllowedImageExtentions();

        // TODO: This pattern can be different. See `image_make_intermediate_size` in image editor implementation.
        $matchFiles = '/([0-9]{1,5})x([0-9]{1,5}).(' . $filetypes . ')$/';

        // Extract the dimensions
        preg_match($matchFiles,$newFilename,$dimensions);

        // If the file URL doesn't allow guessing the dimensions bail out.
        if(empty($dimensions)){
            return $newFilename;
        }

        $newStub = $dimensions[1] . 'x' . $dimensions[2] . '.' . $dimensions[3];

        $oldStub = $size[0] . 'x' . $size[1] . '.' . $dimensions[3];

        $oldFilename = str_replace($newStub,$oldStub,$newFilename);

        return $oldFilename;
    }

    /**
     * Extract all file extensions that match an image/* mime type
     *
     * @return string
     */
    protected function getAllowedImageExtentions(){
        $allowed_filetypes = get_allowed_mime_types();

        $allowed_images = array();

        foreach($allowed_filetypes as $extensions => $mimetype){
            if( substr($mimetype,0,6) == 'image/' ){
                $allowed_images[] = $extensions;
            }
        }

        return implode('|',$allowed_images);
    }


    /**
     * This is the heart of this class. Based on the collected remaps from earlier it does a S&R on the DB.
     */
    public function remap(){

        global $wpdb;

        foreach($this->remaps as $attachment_id => $replaces){

            foreach($replaces as $new_url => $old_data){

                $to_url = wp_get_attachment_image_src($attachment_id,$old_data['size']);
                $to_url = $to_url[0];

                $from_url = str_replace($new_url, $old_data['file'], $to_url);

                // remap urls in post_content
                $wpdb->query( $wpdb->prepare("UPDATE {$wpdb->posts} SET post_content = REPLACE(post_content, %s, %s)", $from_url, $to_url) );

                //TODO: This is disabled as enclosures can't be images, right?
                // remap enclosure urls
                //$result = $wpdb->query( $wpdb->prepare("UPDATE {$wpdb->postmeta} SET meta_value = REPLACE(meta_value, %s, %s) WHERE meta_key='enclosure'", $from_url, $to_url) );

            }

        }

    }

    /**
     * This is a copy of the legacy pre 4.1 wp_constrain_dimensions()
     *
     * @param $dimensions
     * @param $current_width
     * @param $current_height
     * @param $max_width
     * @param $max_height
     *
     * @return array
     */
    public function legacy_wp_constrain_dimensions($dimensions, $current_width, $current_height, $max_width, $max_height){
        if ( !$max_width and !$max_height )
            return array( $current_width, $current_height );

        $width_ratio = $height_ratio = 1.0;
        $did_width = $did_height = false;

        if ( $max_width > 0 && $current_width > 0 && $current_width > $max_width ) {
            $width_ratio = $max_width / $current_width;
            $did_width = true;
        }

        if ( $max_height > 0 && $current_height > 0 && $current_height > $max_height ) {
            $height_ratio = $max_height / $current_height;
            $did_height = true;
        }

        // Calculate the larger/smaller ratios
        $smaller_ratio = min( $width_ratio, $height_ratio );
        $larger_ratio  = max( $width_ratio, $height_ratio );

        if ( intval( $current_width * $larger_ratio ) > $max_width || intval( $current_height * $larger_ratio ) > $max_height )
            // The larger ratio is too big. It would result in an overflow.
            $ratio = $smaller_ratio;
        else
            // The larger ratio fits, and is likely to be a more "snug" fit.
            $ratio = $larger_ratio;

        // Very small dimensions may result in 0, 1 should be the minimum.
        $w = max ( 1, intval( $current_width  * $ratio ) );
        $h = max ( 1, intval( $current_height * $ratio ) );

        // Sometimes, due to rounding, we'll end up with a result like this: 465x700 in a 177x177 box is 117x176... a pixel short
        // We also have issues with recursive calls resulting in an ever-changing result. Constraining to the result of a constraint should yield the original result.
        // Thus we look for dimensions that are one pixel shy of the max value and bump them up
        if ( $did_width && $w == $max_width - 1 )
            $w = $max_width; // Round it up
        if ( $did_height && $h == $max_height - 1 )
            $h = $max_height; // Round it up

        return array( $w, $h );
    }

}

add_filter('import_start',array(new Bugfix31581WPImporter(),'init'));

素敵な仕事+1。
gmazzap

1

大規模なサイトのすべての画像ファイル(およびリンク)の問題をグローバルに完全に解決する-たとえば、個人がWPスタイルを模倣する画像ファイルの名前を手動で変更する可能性があることを考えると、その他の奇妙なバリエーションは難しいかもしれません。データベースの検索と置換の操作も複雑になります(そしてリスクも!)。

エラーの大半-壊れた画像と壊れた画像リンクを処理できますか?

  1. 新しい「ラウンド」メソッドではなく、古い「intval」メソッドによってサイズ変更されたすべてのサイズ変更画像の前の日付を特定します。(別の種類のカットオフも作成できますが、日付が最も簡単なようです。)

  2. 公開されたすべての投稿<=カットオフ日、ロード/レンダリング時にthe_content()でpreg_replaceを実行し、問題のパターンを持つすべての画像ファイルをキャプチャし、それらを目的のパターンに置き換えます。データベースは変更されないままですが、ほとんどの場合、出力にエラーはありません。ソリューションが「単一の」ページ投稿コンテンツとアーカイブページおよびその他のプロセスの両方に適用する必要があるかどうかはわかりません。

このタイプの解決策が役立つ場合、次の質問は問題のパターンと置換を適切に定義できるかどうかです。提案されたソリューションのリストから、いくつかの典型的なパターンが実際に分離されている可能性があります(おそらく、サムネイルやその他の画像を生成する以前のメディア設定から取得されます)。

使用する(およびプラグインに変換する過程にある)単純な関数を既に作成しました。この関数は、指定されたディレクトリ内のすべての画像ファイルを特定の日付までデフォルトの画像または画像リンクでグローバルに置き換えます。上記の方法に従って。これは、著作権に関する過度の注意を払って、オペレーターがすべての画像を削除しただけのサイトで、古いページでresultsい結果を出すことに加えて、それぞれに2つずつ、数千のエラーが発生していることに気づきませんでした画像。

問題のパターンをより具体的に絞り込むことができ、出力を変更する必要がある場合、それを自分のフォーマットにプラグインすることを見ることができます-これは非常に複雑ではなく、私よりも良いRegExerでも簡単です。一方、このアプローチで問題が解決しない場合は、時間を無駄にしたくありません。


これにご協力いただきありがとうございます!ちょっと考えてみてください:DBに間違ったデータがあり、それをディスプレイに表示するだけでは、きれいで持続可能なソリューションではありません。時間を壊す可能性があり、各ビューのパフォーマンスを損ないます。また、他の方法でコンテンツを解析または変更する他のプラグインなど、予測できない副作用がある場合があります。実行方法によっては、バックエンドで画像がまだ壊れています。その場合wp_constrain_dimensions、インポート中に質問で述べたようにスケーリングをリセットするだけで、親指の再構築は控えると思います。
クラフトナー

どういたしまして。つまり、DB内のデータは間違ったデータではなく、新しい体制の下で必要なデータではないということです。パフォーマンスのヒットについては、理論的には日付Xより前の投稿にのみ適用されるため、特に最小になると考えられます。十分な解決策は、サイトの特性、過去の画像処理アプリケーションと習慣、DBのサイズ、実用的および時間的制約などによって異なります。
CKマクラウド

おそらく、万能のソリューションが存在しないことは間違いないでしょう。私は現在、これを処理するさまざまな方法を模索しています。その中には、あなたに似たレンダリング時のアプローチと、これを完全に解決するために私が好むインポート時のアプローチがあります。これがどこにつながるかを見ていきましょう。:)
クラフトナー

1

さて、これは壊れた画像をその場で置き換える基本的な方法です。これは、実証済みのソリューションよりも概念実証に近いことに注意してください。the_contentフィルターにフックするだけで、状況によっては望ましくない副作用が発生する可能性があります。取り扱い注意。:)

それはあまりにもコードでそう言うものの、私は、クレジットたい@Rarstをするために、この答えは私のコードで使用されます。

/*
Plugin Name:  Bugfix 31581 Live
Plugin URI:   /wordpress//a/206986/47733
Description:  Fixes image references in post content after post 4.1 image scaling change (see https://core.trac.wordpress.org/ticket/31581)
Version:      0.0.1
*/

class Bugfix31581Live {

    protected $matchURLs;
    protected $matchFiles;

    protected $remaps;

    public function init(){

        $filetypes = $this->get_allowed_image_extentions();

        $baseurl = wp_upload_dir();
        $baseurl = preg_quote($baseurl['baseurl'], '/');

        $this->matchURLs = '/' . $baseurl . '\/.??([a-zA-Z0-9_-]*?\.(?:' . $filetypes . '))/';

        //TODO: This pattern can be different. See `image_make_intermediate_size` in image editor implementation
        $this->matchFiles = '/([0-9]{1,4})x([0-9]{1,4}).(' . $filetypes . ')$/';

        add_filter('the_content', array($this, 'update_urls') );
    }

    public function update_urls($content){

        $urls = array();

        preg_match_all($this->matchURLs,$content,$urls);

        // Bail out early if we don't have any match.
        if($urls === false || empty($urls[0])){
            return $content;
        }

        // Loop through all matches
        foreach($urls[0] as $url){

            // Try to resolve this URL to an attachment ID
            $id = $this->get_attachment_id($url);

            // If not  let's see if this might be a URL that has been broken by our beloved Changeset 30660
            if( $id === false ){

                $dimensions = array();

                // Extract the dimensions
                preg_match($this->matchFiles,$url,$dimensions);

                // If the file URL doesn't allow guessing the dimensions bail out.
                if(empty($dimensions)){
                    continue;
                }

                // Old filename
                $old = $dimensions[1] . 'x' . $dimensions[2] . '.' . $dimensions[3];

                // New filename (not sure if this exists yet)
                $new = $dimensions[1] . 'x' . ($dimensions[2]+1) . '.' . $dimensions[3];

                // Build the new URL (not sure if this exists yet)
                $new_url = str_replace($old,$new,$url);

                // Try to get the attachment with the new url
                $id = $this->get_attachment_id($new_url);

                // Bad luck. This also doesn't exist.
                if( $id === false ) {
                    continue;
                }

                // Just to be sure everything is in sync we get the URL built from id and size.
                $db_url = wp_get_attachment_image_src($id,array($dimensions[1], $dimensions[2]+1));

                // Check if the URL we created and the one wp_get_attachment_image_src builds are the same.
                if($new_url === $db_url[0]){

                    // Awesome let's replace the broken URL.
                    $content = str_replace($url,$new_url,$content);
                }

            }

        }

        return $content;
    }

    /**
     * Get the Attachment ID for a given image URL.
     *
     * @link   /wordpress//a/7094
     *
     * @param  string $url
     *
     * @return boolean|integer
     */
    protected function get_attachment_id( $url ) {

        $dir = wp_upload_dir();

        // baseurl never has a trailing slash
        if ( false === strpos( $url, $dir['baseurl'] . '/' ) ) {
            // URL points to a place outside of upload directory
            return false;
        }

        $file  = basename( $url );
        $query = array(
            'post_type'  => 'attachment',
            'fields'     => 'ids',
            'meta_query' => array(
                array(
                    'value'   => $file,
                    'compare' => 'LIKE',
                ),
            )
        );

        $query['meta_query'][0]['key'] = '_wp_attached_file';

        // query attachments
        $ids = get_posts( $query );

        if ( ! empty( $ids ) ) {

            foreach ( $ids as $id ) {

                $tmp = wp_get_attachment_image_src( $id, 'full' );

                // first entry of returned array is the URL
                if ( $url === array_shift( $tmp ) )
                    return $id;
            }
        }

        $query['meta_query'][0]['key'] = '_wp_attachment_metadata';

        // query attachments again
        $ids = get_posts( $query );

        if ( empty( $ids) )
            return false;

        foreach ( $ids as $id ) {

            $meta = wp_get_attachment_metadata( $id );

            foreach ( $meta['sizes'] as $size => $values ) {

                $tmp = wp_get_attachment_image_src( $id, $size );

                if ( $values['file'] === $file && $url === array_shift( $tmp ) )
                    return $id;
            }
        }

        return false;
    }

    protected function get_allowed_image_extentions(){
        $allowed_filetypes = get_allowed_mime_types();

        $allowed_images = array();

        foreach($allowed_filetypes as $extensions => $mimetype){
            if( substr($mimetype,0,6) == 'image/' ){
                $allowed_images[] = $extensions;
            }
        }

        return implode('|',$allowed_images);
    }

}

add_filter('init',array(new Bugfix31581Live(),'init'));
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.