PHPで手軽にURLをピュニコード変換する関数を作ってみました(日本語ドメイン⇔Punycode表記)

PHPでピュニコード変換をする方法の紹介です。

PHPで、ピュニコード変換をするには、PEARのライブラリをインストールしないと使えないのかと思っていました。

ただ、フォーラムで同じディレクトリに置くだけで使えるピュニコード変換ライブラリと、便利そうなPHP関数を教えてもらったので、それを組み合わせて「手軽にピュニコード変換できる関数」を作成してみました。

この方法は、Wordpressからも利用できます。

スポンサーリンク
レクタングル(大)広告

ピュニコードとは

Punycode(ピュニコード)とは、国際化ドメイン名(今回の例では日本語ドメイン名)を、英数字とハイフンの組み合わせで表現できるように変換する技術の事です。

例えば、日本語ドメインで、「あいうえお.com」というドメインがあったら、ピュニコード変換を行うと以下のようになります。

日本語ドメイン:あいうえお.com

ピュニコード変換後:xn--l8jegik.com

PHPでは、PEARのNet_IDNA2を使用すれば、変換ライブラリを使うことができます。ただ、これだとサーバーにPEARライブラリをインストールする必要があり、手軽に使えませんでした。

訂正:ぺんたんさんにコメントで教えてもらったのですが、PEARをインストールせずともNet_IDNA2をダウンロードしてインクロードパスの設定をすれば利用できるそうです。

けれど今回、同ディレクトリに置いて利用できるPunycodeライブラリがあることを教えてもらったので、それを使って、URLのドメイン部分だけをピュニコード変換する関数を作成してみました。

利用に必要な手順

利用するには、以下の手順が必要です。

  1. Punycodeライブラリをダウンロード&設置
  2. ピュニコード関数を貼り付ける
  3. ピュニコード変換関数を利用する

関数を貼り付けて利用するだけなので、「日本語ドメインを含むURL←→ピュニコードを含むURL」の変換が手軽にできると思います。

Punycodeライブラリをダウンロード&設置

まずは、true/php-punycode · GitHubからライブラリをダウンロードします。

ピュニコード変換ライブラリ

ダウンロードした「php-punycode-master.zip」ファイルを解凍して中にある以下のファイルを取り出します。

  1. src/Punycode.php

利用するのは、Punycode.phpファイルのみです。取り出したPunycode.phpファイルをコードを書くPHPファイルと同ディレクトリに置いてください。

ピュニコード関数を貼り付ける

で、Punycodeライブラリを利用して作成した関数が以下。これを、PHPコード内に貼り付けます。Wordpressとかだったら、functions.phpとかでOKです。

///////////////////////////////////////
//http_build_url関数の代わり
//http://php.net/manual/ja/function.http-build-url.php
///////////////////////////////////////
//参考:http://mio-koduki.blogspot.jp/2012/05/php-httpbuildurl.html
//PECLのhttp_build_urlがある可能性があるためチェックする
if(!function_exists('http_build_url')){
  //フラグの定数を設定
  define('HTTP_URL_REPLACE',1);
  define('HTTP_URL_JOIN_PATH',2);
  define('HTTP_URL_JOIN_QUERY',4);
  define('HTTP_URL_STRIP_USER',8);
  define('HTTP_URL_STRIP_PASS',16);
  define('HTTP_URL_STRIP_AUTH',24);
  define('HTTP_URL_STRIP_PORT',32);
  define('HTTP_URL_STRIP_PATH',64);
  define('HTTP_URL_STRIP_QUERY',128);
  define('HTTP_URL_STRIP_FRAGMENT',256);
  define('HTTP_URL_STRIP_ALL',504);
  function http_build_url($url,$parts=array(),$flags=HTTP_URL_REPLACE,&$new_url=array())  {
    //置き換えるキー
    $key=array('user','pass','port','path','query','fragment');
    //urlをパースする
    $new_url=parse_url($url);
    //スキーマとホストが設定されていれば置き換える
    if(isset($parts['scheme'])) {
      $new_url['scheme']=$parts['scheme'];
    } if(isset($parts['host'])) {
      $new_url['host']=$parts['host'];
    }
    //フラグにHTTP_URL_REPLACEがあれば置き換える
    if($flags&HTTP_URL_REPLACE) {
      foreach($key as $v) {
        if(isset($parts[$v])) {
          $new_url[$v]=$parts[$v];
        }
      }
    } else {
      //フラグにHTTP_URL_JOIN_PATHがあり新しいパスがあれば新しいパスをつなげる
      if(isset($parts['path'])&&$flags&HTTP_URL_JOIN_PATH) {
        if(isset($new_url['path'])) {
          $new_url['path']=rtrim(preg_replace('#'.preg_quote(basename($new_url['path']),'#').'$#','',$new_url['path']),'/').'/'.ltrim($parts['path'],'/');
        } else {
          $new_url['path']=$parts['path'];
        }
      }
      //フラグにHTTP_URL_JOIN_QUERYがあり新しいクエリがあれば新しいクエリをつなげる
      if(isset($parts['query'])&&$flags&HTTP_URL_JOIN_QUERY) {
        if(isset($new_url['query'])) {
          $new_url['query'].='&'.$parts['query'];
        } else {
          $new_url['query']=$parts['query'];
        }
      }
    }
    //ストリップフラグの判定をし、設定されていれば消す
    foreach($key as $v) {
      if($flags&constant('HTTP_URL_STRIP_'.strtoupper($v))) {
          unset($new_url[$v]);
      }
    }
    //パーツを繋げて返す
    return (isset($new_url['scheme'])?$new_url['scheme'].'://':'').(isset($new_url['user'])?$new_url['user'].(isset($new_url['pass'])?':'.$new_url['pass']:'').'@':'').(isset($new_url['host'])?$new_url['host']:'').(isset($new_url['port'])?':'.$new_url['port']:'').(isset($new_url['path'])?$new_url['path']:'').(isset($new_url['query'])?'?'.$new_url['query']:'').(isset($new_url['fragment'])?'#'.$new_url['fragment']:'');
  }
}

//Punycode変換ライブラリを読み込む
include 'Punycode.php';
//Punycodeをインポート
use True\Punycode;
///////////////////////////////////////
//Punycode変換関数
///////////////////////////////////////
function convert_punycode($url, $is_encode = true){
  $url_parts = parse_url($url);
  $Punycode = new Punycode();
  if ( $is_encode ) {
    $host = $Punycode->encode($url_parts['host']);
  } else {
    $host = $Punycode->decode($url_parts['host']);
  }
  $url_parts['host'] = $host;
  return http_build_url($url, $url_parts);
}

///////////////////////////////////////
//Punycodeへの変換(エンコード)
///////////////////////////////////////
function punycode_encode($url){
  return convert_punycode($url, true);
}

///////////////////////////////////////
//通常のURLへ戻す(PreCode)
///////////////////////////////////////
function punycode_decode($url){
  return convert_punycode($url, false);
}

上記コードで利用しているuseは、PHP5.3未満を利用しているサーバーでは、利用できません。5.3未満を利用している場合は、削除して利用するなどする必要があります。

簡単に説明すると、URLとドメインを分解するのにPHPのparse_url関数を利用しています。

ただ、分解したURLパーツを結合するのにhttp_build_url関数を利用したいところですが、PECL 拡張モジュールのインストールが必要なことから、http_build_url関数を自作したものを利用しています。

自作のhttp_build_url関数は、以下のサイトを参考にさせていただきました。

参考 メモ: [PHP] http_build_urlを実装する

あとは、Punycodeライブラリを呼び出して、変換関数を書いただけです。

実際に利用するのは、以下の2つの関数だけです。

  • punycode_encode(日本語込URL→ピュニコード込URL)
  • punycode_decode(ピュニコード込URL→日本語込URL)

ピュニコード変換関数を利用する

それぞれの、関数の利用例は、以下のようになります。

///////////////////////////////////////
//日本語ドメインの変換
///////////////////////////////////////
//変換するURL
$url = 'https://あいうえお.com/aaa/bbb/';
//Punycodeへのエンコード
$encoded_url = punycode_encode($url);
var_dump( $encoded_url );
//[OUT]:https://xn--l8jegik.com/aaa/bbb/

//Punycodeからデコード
$decoded_url = punycode_decode($encoded_url);
var_dump( $decoded_url );
//[OUT]:https://あいうえお.com/aaa/bbb/

///////////////////////////////////////
//日本語ドメインの変換
///////////////////////////////////////
//変換するURL
$url = 'http://こんにちは.jp/wordpress-child-theme';
//Punycodeへのエンコード
$encoded_url = punycode_encode($url);
var_dump( $encoded_url );
//[OUT]:http://xn--28j2a3ar1p.jp/wordpress-child-theme

//Punycodeからデコード
$decoded_url = punycode_decode($encoded_url);
var_dump( $decoded_url );
//[OUT]:http://こんにちは.jp/wordpress-child-theme

///////////////////////////////////////
//通常ドメインの変換(変わらない)
///////////////////////////////////////
//変換するURL
$url = 'https://nelog.jp/wordpress-child-theme';
//Punycodeへのエンコード
$encoded_url = punycode_encode($url);
var_dump( $encoded_url );
//[OUT]:https://nelog.jp/wordpress-child-theme

//Punycodeからデコード
$decoded_url = punycode_decode($encoded_url);
var_dump( $decoded_url );
//[OUT]:https://nelog.jp/wordpress-child-theme

PHPで実行してみると、URLの日本語ドメイン部分だけ、ピュニコードに変換されて出力されます。

逆に、ピュニコードを含むURLをデコードした場合、ピュニコード部分のみが日本語ドメインに戻して出力されます。

通常のドメインの場合は、「nelog.jp⇔nelog.jp」と変わりがありがません。

ピュニコードの確認は以下などで行ってみてください。

日本語.jpは、株式会社日本レジストリサービス(JPRS)が日本語JPドメイン名の魅力をお伝えするサイトです。

まとめ

Twitterのツイート機能などを独自実装するときは、日本語ドメインを使用したページでは、うまく動作しなかったりします。(多分)

その他にも、全て半角英数字などのURLでないと、動作しないAPIとか、サービスとかもまだまだあると思います。

そんな時に、今回の関数は、サーバーにライブラリをインストールする必要なく利用できるので、結構手軽なのではないかと思います。

『PHPで手軽にURLをピュニコード変換する関数を作ってみました(日本語ドメイン⇔Punycode表記)』へのコメント

  1. 名前:通りすがり 投稿日:2015/05/28(木) 13:48:40 ID:2e77c7a35

    こんにちは。
    フォーラムにも書きましたが、このプログラムはPHP5.1.6以下だと動作しないようです。
    参考までに。

  2. アバター画像 名前:わいひら 投稿日:2015/05/28(木) 14:36:20 ID:8548f8ac0

    報告ありがとうございます!
    Simplicityの方も修正し、記事も注意事項を追記しておきました。

  3. 名前:ぺんたん 投稿日:2016/04/05(火) 03:41:24 ID:7ba1f97bf

    >サーバーにPEARライブラリをインストールする必要があり
    これは間違い
    Net_IDNA2をダウンロードしてインクロードパスの設定をすれば使えます

  4. アバター画像 名前:わいひら 投稿日:2016/04/05(火) 08:04:07 ID:5a010f1ec

    そういった方法でも使えたんですね。知りませんでした;
    記事の方を、修正しておきたいと思います。
    教えていただき、ありがとうございます!