web-dev-qa-db-ja.com

Termに特殊文字が含まれているときにWP_Query tax_queryをエスケープする

これが私の問題です。カスタム分類法を適用するカスタム投稿タイプがあります。この分類法により、管理者はユーザーが検索したときに投稿が確実に見つかるような検索候補を提供できます。この分類法は、フロントエンドでこれらの提案で検索フィールドを自動補完するためにも使用されます。

用語がスラッシュ、スペースまたは他の「特殊」文字を含む場合を除いて、システムは正常に機能します。

<?php
$keyword = 'Ski-in%2FSki-out';//Submitted via $_GET
$keyword = urldecode($keyword); // (string)'Ski-in/Ski-out'

$taxQuery = new WP_Query(array(
    'post_type' => 'ml_properties',
    'tax_query' => array(
        'relation' => 'OR',
        array(
            'taxonomy' => 'mc_tax_lifestyle',
            'field' => 'name',
            'terms' => array($keyword),
            'operator' => 'IN'
        )
    )
));
?>

私はWordPressによって提供されているエスケープ機能を使用することを検討しました、しかしそこに運がありませんでした。これは単純な問題になると思いましたが、私のGoogle-fuは強力ではないか、または運がないだけです。

Ski-in/Ski-outがMySQLに格納されている正確な文字列であることも確認しました。 esc_attr()$wpdb->esc_like()esc_sql()の使い方を調べましたが、効果がありません。

明らかなものが足りないのでしょうか。

5
Philip Downer

これがバグかどうかはわかりませんが、さらに調査が必要です。 tax_querynameフィールドで簡単なテストをいくつか実行しました。用語名に特殊文字が含まれる場合や、複数のWordが含まれる場合は、tax_queryがSQLクエリから除外されます。

テスト1

ここでは2つの用語を使用しています。あなたの用語はSki-in/Ski-out、私のテストサイトの用語はUit die koskasです。今、私は私のカスタムクエリを次のように実行すると

$taxQuery = new \WP_Query(array(
    'post_type' => 'post',
    'tax_query' => array(
        array(
            'taxonomy' => 'category',
            'field' => 'name',
            'terms' => 'Ski-in/Ski-out',
            'operator' => 'IN'
        )
    )
));
?><pre><?php var_dump($taxQuery->request); ?></pre><?php    

リクエストのvar_dump()は私にこれを与えます

string(254) "SELECT SQL_CALC_FOUND_ROWS  wp_posts.ID 
FROM wp_posts  
WHERE 1=1  
AND ( 
      0 = 1
    ) 
AND wp_posts.post_type = 'post' 
AND (wp_posts.post_status = 'publish' 
OR wp_posts.post_status = 'private') 
GROUP BY wp_posts.ID 
ORDER BY wp_posts.post_date DESC 
LIMIT 0, 4"

tax_queryはSQLクエリに追加されません

テスト2

特殊文字を使用せずに単一のWord用語を使用すると、クエリは機能します。ここで私はOngekategoriseerdと呼ばれる用語でテストしました

$taxQuery = new \WP_Query(array(
    'post_type' => 'post',
    'tax_query' => array(
        array(
            'taxonomy' => 'category',
            'field' => 'name',
            'terms' => 'Ongekategoriseerd',
            'operator' => 'IN'
        )
    )
));
?><pre><?php var_dump($taxQuery->request); ?></pre><?php

これは私に正しいSQLクエリを与えます

string(378) "SELECT SQL_CALC_FOUND_ROWS  wp_posts.ID FROM wp_posts  
INNER JOIN wp_term_relationships 
ON (wp_posts.ID = wp_term_relationships.object_id) 
WHERE 1=1  
AND ( 
      wp_term_relationships.term_taxonomy_id 
IN (1)
    ) 
AND wp_posts.post_type = 'post' 
AND (wp_posts.post_status = 'publish' 
OR wp_posts.post_status = 'private') 
GROUP BY wp_posts.ID 
ORDER BY wp_posts.post_date DESC 
LIMIT 0, 4" 

これが意図的なものなのかバグなのかはまだわかりませんが、それまでの間にWP_Queryソースコードを見て、tax_queryがどのようにビルドされているかを確認してください。私は近い将来それについても調べるでしょう

アップデート1

仕事に出る前に、私はすぐにWP_Queryクラスを調べました。クラスの終わりに向かって、WP_Queryはいくつかの後方互換性テストをします、そして目に見える価値からこれがすべてが失敗するところであるように思われるかもしれません。

私はこの問題についてのtracチケットを見つけることができません、もし誰かがリンクを持っているならば、私の答えを更新するか、またはコメントで投稿してください。

WORKAROUND

名前を使う必要があり、使わなければならない場合は、 get_term_by() を使用して自分でヘルパー関数を作成する必要があります。その後、用語nameを使用して用語objectを取得し、そこから用語IDを使用してtax_queryで使用できます。

アップデート2

それを見つけた

問題はWP_Query自体にはありませんでした( これらのクラスを見ているとなんてめちゃくちゃになります )。 WP_QueryWP_Tax_Query を使用してtax_queryを構成します。 do_actionparse_tax_queryメソッドのWP_Query呼び出しの直前の最後の行を確認してください。

$this->tax_query = new WP_Tax_Query( $tax_query );

WP_Tax_Queryクラスに移動しました。このクラスには、単一のクエリをあるフィールドから別のフィールドに変換する次のメソッド、 transform_query があります。 fieldパラメータをnameに設定すると、すべてが壊れます。

fieldnameに設定すると、名前は sanitize_title_for_query を使用してサニタイズされます。

$terms = "'" . implode( "','", array_map( 'sanitize_title_for_query', $query['terms'] ) ) . "'";

これはスラッシュを取り除き、空のスペースをハイフンに変換します。つまり、Ski-in/Ski-outski-inski-outに変換され、Uit die koskasuit-die-koskasに変換されます。あなたの用語名は無効なので、以下のクエリは用語IDとその子を取得します。

$terms = $wpdb->get_col( "
    SELECT $wpdb->term_taxonomy.$resulting_field
    FROM $wpdb->term_taxonomy
    INNER JOIN $wpdb->terms USING (term_id)
    WHERE taxonomy = '{$query['taxonomy']}'
    AND $wpdb->terms.{$query['field']} IN ($terms)
" );

失敗し、空の配列を返します

array(0) {
}

結論

私見、ここでの衛生は間違っていて、単一のスペースとスラッシュを可能にするより適切な方法と取り替えられるべきです。用語名を正しく使用できない場合にnameフィールドを使用する目的は何ですか。この公衆衛生は、上記の場合にtax_query内のnameフィールドの使用を中止します。

すでに述べたように、用語名を使用する必要がある場合の最善の方法は、get_term_by()を使用して用語からIDを取得し、そのIDをtax_queryで使用するというヘルパー関数を作成することです。

アップデート3

コメント内の@manifestphilのおかげで、 changeset#31346 がこのまさしくクレイジーな過剰サニタイズ問題にあります。これが将来のリリースで修正されることを願います。

6
Pieter Goosen