ブログ用タグ: URLエンコード

  • WordPress の – 問題

    現在のページをメールやX(Twitter)で共有するとき、サイト名の
    「投稿タイトル – *ListFreak」が
    「投稿タイトル – *ListFreak」
    になってしまう問題とその解決についての覚え書き。

    問題

    たとえば現在のページのタイトルを件名に入れてメーラーを立ち上げる処理のため、次のような文字列を作りたいとする。

    mailto:?subject=件名

    実際のコードは次のようになる。

    $subject = wp_get_document_title(); // 投稿タイトル – *ListFreak
    $mailto  = 'mailto:?subject='.rawurlencode( $subject );

    このURLにアクセスすると、件名が
    「投稿タイトル – *ListFreak」
    になってしまう。

    原因

    WordPress はページタイトルとサイトタイトルをハイフン( – )でつないでHTMLのタイトルを生成する。

    それを出力する際に、テキストのあれこれを読みやすくする wptextualize() という関数が、ハイフンをエンダッシュ( – )に変換する。エンダッシュはハイフンよりも少し長く、読みやすい。

    wptextualize() はHTMLタグを指定して、あるいはまったくオフにすることができる。ハイフンであれば文字化けも起きないのだが、せっかく読みやすく変換してくれたので、これを生かしたい。

    文字化けの原因はエンダッシュそのものでなく、エンダッシュがHTMLエンティティとして出力されること。実際、表示されているページのソースを見ると次のようになっている。– がエンダッシュのHTMLエンティティ。

    <title>*ListFreak &#8211; 美しい箇条書き</title>

    Webページに確実に表示させるという観点からは適切な処理なのかもしれない。しかしこのせいで面倒なことになる。

    例えば下の2つの文字列は同じように見えるが、$s1のエンダッシュがUTF-8であるのに対して、$s2のエンダッシュはHTMLエンティティである。興味のある方はこのページのソースで確認されたし。

    // 投稿A - サイトB
    $s1 = 'A – B';
    $s2 = 'A – B';

    $s1はURLエンコードしてデコードすると元に戻る。

    $s1 = 'A – B';
    
    echo $s1_enc     = rawurlencode( $s1 )."\r\n";
    echo $s1_enc_dec = rawurldecode( $s1_enc )."\r\n";
    
    実行結果↓
    A%20%E2%80%93%20B
    A – B

    しかし$s2はURLエンコードしてデコードしても元に戻らない。

    $s2 = 'A – B';
    
    echo $s2_enc     = rawurlencode( $s2 )."\r\n";
    echo $s2_enc_dec = rawurldecode( $s2_enc )."\r\n";
    
    実行結果↓
    A%20%26%238211%3B%20B
    A &#8211; B

    なぜこうなるか。rawurlencode() が準拠している標準 (RFC 3986) では渡される文字列が UTF-8 であることを前提としているから、らしい。

    rawurlencode() は &#8211; をHTMLエンティティでなく普通の(UTF-8の)文字列とみなしてエンコードする。つまり & => %26, # => %23, 8211, ;=>%3B と変換されてしまう。

    解決

    HTMLエンティティ混じりの文字列を、UTF-8に直してあげればOK。

    $s2 = 'A – B';
    $s2 = html_entity_decode( $s2 );
    
    echo $s2_enc     = rawurlencode( $s2 )."\r\n";
    echo $s2_enc_dec = rawurldecode( $s2_enc )."\r\n";
    
    実行結果↓
    A%20%E2%80%93%20B
    A – B

    最初のコード例はこうなる。

    $subject = wp_get_document_title();
    $subject = html_entity_decode( $subject );
    $mailto  = 'mailto:?subject='.rawurlencode( $subject );