アカベコマイリ

HEAR NOTHING SEE NOTHING SAY NOTHING

PHP で祝日列挙

友人より祝日の情報をネットから取得したいが良い方法はないか?という質問があり、私も興味があったので調べてみた。ググって初めにみつけたものは以下の質問。

色々なサービスはあるけれど省庁の運用する公的なものは見当たらない。次にカレンダー系のサービスをあたってみたところ Google Calendar API がよさそうな感じ。

この API を使用した PHP サンプルは Zend Framework の Zend Gdata を用いたものが大半である。PHP 的にはこのフレームワークに乗るのが一般的なのだろう。

しかし今回は PHP の勉強によさそうなので、直接 API を利用するスクリプトを書いてみる事にした。Eclipse PDT とデバッガ使用の練習にもなる。

はじめに

今回作成したスクリプトは、WordPress のプラグインとして動作するようにした。

このスクリプトをプラグインとして登録してから記事にショートコードを書くと、単純な ul - li のリストとして祝日の年月日と名前 (昔は日本語だったそうだが今は英語になっている) が記事に埋め込まれる。

[HOLIDAY year="2009" month="10" limit="100"]

実装

実装は以下のようになる。XAMPP で動作確認済み。仕様についてはソース中にコメントとして記述した。

<?php
/*
Plugin Name: WP Holiday
Plugin URI:
Description: Google Calndar API で祝日を取得します。
Version: 0.1
Author: アカベコ
Author URI: https://akabeko.me/
*/

/**
 * 指定された年、月の祝日リストを取得します。
 *
 * @param[in] $atts ショートコードの引数。year 省略時は今年、month 省略時は当月、limit 省略時は 100 件として扱います。
 * @return HTML 形式の祝日情報文字列。
 */
function getHoliday( $atts ) {
  extract( shortcode_atts(array('year' => '', 'month' => '', 'limit' => '' ), $atts ) );

  $dateBegin = "";
  $dateEnd   = "";
  $query     = makeQuery( $year, $month, $limit, $dateBegin, $dateEnd );
  if ( $query == "" ) {
    return "<p><strong>WP-Holiday Error</strong> : 年と月の指定に問題があります。</p>";
  }

  $feed = "http://www.google.com/calendar/feeds/japanese@holiday.calendar.google.com/public/full" . "?" . $query;
  $xml  = simplexml_load_file( $feed );
  if ( !$xml ) {
    return "<p><strong>WP-Holiday Error</strong> : XML の読み込みに失敗しました。</p>";
  }

  $ret = "<p>" . $dateBegin . " ~ " . $dateEnd . "の祝日</p>" . enumHoliday( $xml );
  return $ret;
}

/**
 * 祝日情報を列挙します。
 *
 * @param $xml カレンダー情報を読み込んだ XML オブジェクト。
 * @return HTML 形式の祝日情報文字列。
 */
function enumHoliday($xml) {
  $ret = "\n<ul>\n";
  foreach ( $xml->entry as $entry ) {
    $gd         = $entry->children("http://schemas.google.com/g/2005");
    $attributes = $gd->when->attributes();

    // $gd->when[ 'startTime' ] だと何故か null が返るので、属性を検索する
    foreach ( $attributes as $name => $value) {
      if ( $name == "startTime" ) {
        $ret .= "<li>" . $value . " : " . $entry->title . "</li>\n";
        break;
      }
    }
  }

  $ret .= "</ul>\n";
  return $ret;
}

/**
 * 指定された年、月から Google Calendar API 用クエリを生成します。
 *
 * @param[in] $year 年。省略時は、今年として扱います。
 * @param[in] $month 月。省略時は、当月として扱います。
 * @param[in] $limit 最大取得数。省略時は 100 件として扱います。
 * @param[out] $dateBegin 祝日列挙の開始日。
 * @param[out] $dateEnd 祝日列挙の終了日。
 * @return  成功時はクエリ文字列。失敗時は空文字。
 */
function makeQuery( $year, $month, $limit, &$dateBegin, &$dateEnd ) {
  // 開始・終了の範囲設定
  if ( $year == "" ) {
    $dateBegin = date( "Y-m-01" );
    $dateEnd   = date( "Y-m-t"  );
  } else if ( $month == "" ) {
    if ( !checkdate( 1, 1, $year ) ) {
      return "";
    }

    $dateBegin = date( "Y-01-01", mktime( 0, 0, 0,  1, 1, $year ) );
    $dateEnd   = date( "Y-12-t",  mktime( 0, 0, 0, 12, 1, $year ) );
  } else {
    if ( !checkdate( $month, 1, $year ) ) {
      return "";
    }

    $dateBegin = date( "Y-m-01", mktime( 0, 0, 0, $month, 1, $year ) );
    $dateEnd   = date( "Y-m-t",  mktime( 0, 0, 0, $month, 1, $year ) );
  }

  return "start-min=" . $dateBegin . "&start-max=" . $dateEnd . "&max-results=" . ( is_numeric( $limit ) ? $limit : "100" );
}

add_shortcode( 'HOLIDAY', 'getHoliday' );
?>

出力例は以下。月まで指定すると出力数が少ないため year だけ 2009 を指定して、2009 年の全祝日を出してみた。

<p>2009-01-01 ~ 2009-12-31の祝日</p>
<ul>
<li>2009-12-23 : The Emperor's Birthday</li>
<li>2009-04-29 : Showa Day</li>
<li>2009-09-21 : Respect for the Aged Day</li>
<li>2009-01-01 : New Year's Day</li>
<li>2009-02-11 : National Foundation Day</li>
<li>2009-07-20 : Marine Day</li>
<li>2009-11-23 : Labour Thanksgiving Day</li>
<li>2009-10-12 : Health and Sports Day</li>
<li>2009-05-04 : Greenery Day</li>
<li>2009-11-03 : Culture Day</li>
<li>2009-05-03 : Constitution Memorial Day</li>
<li>2009-01-12 : Coming-of-Age Day</li>
<li>2009-05-05 : Children's Day</li>
</ul>

課題

今回のスクリプトは最低限の機能しかないので、課題が色々とある。機会があれば修正するかもしれないので、ぱっと思いついたものをメモっておく。

  • データが取得されたままの順番で出力されている

    • ソートすべきだろう
  • 呼び出しの度にデータ取得が行われる

    • 祝日の更新頻度は低いはず
    • 更新がなければキャッシュを表示したい
  • 出力状態が貧弱

    • AmazonLink のようなテンプレ機能が欲しい
  • 管理画面がない

    • テンプレ機能とか入れたら必要になるだろう