【PHP】iCal形式のファイルから出力制限して外部カレンダーと連携するサンプル

社内の予定表をiCal形式のファイルで管理しているけど、iPhone/iPod touchのカレンダーやGoogleカレンダーと連携するために読み込むとファイルサイズが大きくて時間がかかってしまうので、イベント数を制限して出力できないかと調査したメモ。

まずiCalendar(vCalendar)形式のファイルをPHPでパースできないかと調べてみると、PearのFile_IMCで読み込めて配列に格納できるみたい。

実際に使ってみると確かに連想配列形式にしてくれるけど、全てパースしていると遅いし、編集した結果を出力できないので使いづらい。

しょうがないので自力で正規表現を使ってやってみた。

以下ソース。

<?php
/**
 * ical形式のファイルからイベント情報を制限して出力する
 * 外部のカレンダー(Google カレンダーやiPhone)用
 *   デフォルトイベント数:100
 * limitに負の値を指定すれば全てのイベント
 */

//パラメータからicalファイル名を取得
$sFileName = '';
if (isset($_GET['ical'])) {
    if (strlen($_GET['ical']) > 0 ) {
        $sFileName = $_GET['ical'];
    }
    if (strpos($sFileName, '.ics') === false) { //拡張子がicsファイルのみ
        $sFileName .= '.ics';
    }
}
//パラメータから制限数を取得
$iLimit = 100;
if (isset($_GET['limit'])) {
    if (is_numeric($_GET['limit'])) {
        $iLimit = $_GET['limit'];
    }
}

$sCal = getLimitCal($sFileName, $iLimit);
if (strlen($sCal) > 0) {
    header('Content-Type: text/calendar; charset=utf-8');
    echo $sCal;
}

/**
 * iCalファイルをパースする
 * @param  string $sFilePath iCalパス
 * @param  string $iLimit    イベント制限数
 * @return string            ical形式の文字列
 */
function getLimitCal($sFilePath, $iLimit=100) {
    $sReturn = '';
    if (file_exists($sFilePath)) {
        $sContents = file_get_contents($sFilePath); //ファイルを読み込む

        //正規表現でイベントを抜き出す
        //$sRegex = '(?P<header>((?!BEGIN:VEVENT).)+)BEGIN:VEVENT(?P<event>((?!END:VEVENT).)+)END:VEVENT';
        $sRegex = 'BEGIN:VEVENT((?!END:VEVENT).)+END:VEVENT';
        if (preg_match_all( "/" . $sRegex . "/is", $sContents, $aMatch) ) {
            $aLimit = $aMatch[0];
            if ($iLimit > 0) { //出力制限(ループしながらパースすれば○週間前とかも出来る)
                $aLimit = array_slice($aLimit, -$iLimit);
            }
            //正規表現でイベントを置き換える
            $sRegex = 'BEGIN:VEVENT.+END:VEVENT';
            //$sReturn = preg_replace("/" . $sRegex . "/is", '1', $sContents);
            $sReturn = preg_replace("/" . $sRegex . "/is", implode("\n", $aLimit), $sContents);
            //echo '<textarea rows=30 cols=100>' . $sReturn . '</textarea>';
        }
    }
    return $sReturn;
}

/**
 * デバッグ関数
 */
function a() {
    if (!class_exists('dBug', false)) {
        require_once ('dBug.php');
    }
    foreach (func_get_args() as $v) new dBug($v);
}

正規表現をもうちょっと工夫すれば、もっとスマートに書けそう。デバッグ用のコメントや関数が残ってるけど気にしない。

これを利用してcronでicsファイルをバックアップをとりながら、ファイル自体を軽くする処理を書けばThunderbirdで書き込んでいるカレンダーの表示自体が早くなり、もっと幸せになることに気づいたけど、それはまた今度。

※Todoを管理している場合はこのプログラムではダメ。

▼関連記事

Thunderbird + Lightningでスケジュール管理
Google カレンダーでiCal形式を読み込むと文字化け
GoogleカレンダーにだけiCal形式のファイル参照を許可する