PHPでナビゲーションバー生成

ページ情報
制作日
2004-01-21
最終更新日
2004-01-21
参照用URI
http://www.arielworks.net/articles/2004/0121a
分野

スクリーンショット:PHPで生成したナビゲーションバー

このサイトの上下にはスクリーンショットのようなナビゲーションバーが付いて現在地を把握することが出来ます。こういったサイト内で統一されたナビゲーションバーを元になるXMLやHTML文章に直に書いてしまった場合、上位のディレクトリのファイル名が変わったときなどに一気に複数のファイルを書き換える必要が有るため不便です。

そこでこのサイトではPHPでXMLをXHTMLに変換する際に自動生成しています。今回はその方法を紹介します。

get_vd()

//-------------------------------------------------------------------------
// array get_vd( string TargetPath [, string Title] )
// TargetPathはルートからのパス。「/works/fb2k/」など
// Titleは一番最初のタイトルに限りタイトルを直接持ち込める。
//-------------------------------------------------------------------------
get_vd( $target, $title_first = "", $loop = 0 ) {

    // 無限ループはまずいので適当にカット
    if ($loop > 20) {
        exit_with_xml( '無限ループになっています。' );
    }


    // ディレクトリ名だった場合は/index.xmlを付ける
    if( substr( $target, -1, 1 ) == '/' ) {
        $target .= 'index.xml';
    } elseif ( is_dir( $_SERVER['DOCUMENT_ROOT'] . $target ) ) {
        $target .= '/index.xml';
    }

    $vdir_target = '';
    $title_target = '';

    // ファイルがあるかチェック。
    // 有った場合はvirtual_directoryとタイトルをゲットする
    if ( file_exists( $_SERVER['DOCUMENT_ROOT'] . $target ) ) {

        if ( $handle_target = fopen( $_SERVER['DOCUMENT_ROOT'] . $target, "r" ) ) {
            flock( $handle_target, LOCK_SH );
            $data_target = fread( $handle_target, filesize( $_SERVER['DOCUMENT_ROOT'] . $target ) );
            flock( $handle_target, LOCK_UN );
            fclose( $handle_target );
        }

        // virtual_directoryを取得
        if ( preg_match( "/<link rel=\"up\" href=\"(.*?)\"\/>/", $data_target, $regs ) ) {
            $vdir_target = $regs[1];
        }
        // タイトルを取得。
        if ( preg_match( "/<title.*?>(.*?)<\/title>/is", $data_target, $regs ) ) {
            $title_target = $regs[1];
        }

    // ファイルがなかったら現在のパスからゲットする。
    } else {

        // 分割して最後の一個はタイトルに、再結合してvdirに
        $target_exp = explode( '/', $target );
        array_pop( $target_exp );
        $title_target = array_pop( $target_exp );
        $vdir_target = implode( '/', $target_exp );

    }

    // ループが0回目の時は持ち込まれたものを使う
    if( $loop == 0 && $title_first != "" ) {
        $title_target = $title_first;
    }

    // hoge - は削除
    if( preg_match( "/^.*? - (.*)$/", $title_target, $tit ) ) {
        $title_target = $tit[1];
    }

    // 親取得ルーチン
    // vdir_target が空だったら、一個上かそのディレクトリのindex.xml
    if( $vdir_target == "" ) {

        // index.xml以外だったらそのディレクトリを取得する
        if( basename( $target, '.xml' ) != 'index' ) {
            $dir_oya = dirname( $target );
        } else {
            // 現在のディレクトリを分割
            $temp_oya = explode( "/", $target );
            // 親ディレクトリは一個上
            // よってファイル名とディレクトリ名で2個減らしてくっつけ直す
            array_pop( $temp_oya );
            array_pop( $temp_oya );
            // implodeするとき、/だけだと消えるので
            if( count( $temp_oya ) == 1 ){
                $dir_oya = '/';
            } else {
                $dir_oya = implode( "/", $temp_oya );
            }
        }

    // 値があった場合はそれを使用
    } else {

        $dir_oya = $vdir_target;

    }

    // dir_oya が有ると言うことはまだ上があるってことだ
    if ( $dir_oya ) {
        $oya = get_vd( $dir_oya, NULL, $loop + 1 );
        foreach( $oya as $value ) {
            $result[ $value['depth'] ] = $value;
        }
    }

    // 値に現在の値を代入
    // roopが0の時はその場所なのでリンクは不要
    // ファイルがないときもリンクは無し
    if( $loop != 0 && file_exists( $_SERVER['DOCUMENT_ROOT'] . $target ) ){
        if( basename( $target, '.xml' ) == 'index' ) {
            $target = preg_replace( "/^(.*\/).+?$/i", '$1', $target );
        }
        $result[ $loop ] = array(
            'path' => $target,
            'subject' => $title_target,
            'depth' => $loop
        );
    } else {
        $result[ $loop ] = array(
            'path' => '',
            'subject' => $title_target,
            'depth' => $loop
        );
    }
    return $result;

}

少し長めで読みにくいですが行っていることは以下の通りです。

  1. 指定されたパス(ディレクトリ名だったら「index.xml」を追加)のXMLファイルを開き正規表現でtitle要素内の値を取得する。
  2. もしrel属性が「up」のlink要素が有る場合はそのパスを取得しておく。
  3. もしファイルが存在しなかったらディレクトリ名をタイトルとして扱いお茶を濁す。
  4. 先ほどのlink要素が有った場合はそのパスを上位ファイルとして扱い、無かった場合は単純に1つ上のディレクトリ(index.xml)を上位ファイルとして扱う。
  5. 再帰処理で繰り返してルートディレクトリまで到達したら処理完了。パス、タイトル、階層の深さを配列に格納して返す。

たとえばget_vd( "/works/fb2k/install/" )とすると

Array
(
    [3] => Array
        (
            [path] => /
            [subject] => Arielworks.net
            [depth] => 3
        )

    [2] => Array
        (
            [path] => /works/
            [subject] => Works
            [depth] => 2
        )

    [1] => Array
        (
            [path] => /works/fb2k/
            [subject] => foobar2000
            [depth] => 1
        )

    [0] => Array
        (
            [path] => 
            [subject] => インストールと日本語化
            [depth] => 0
        )

)

このような配列が帰ってきます。

文章としての独立性を持たせるため「foobar2000 - インストールと日本語化」というタイトルがXML文章には与えられていますが、このままだと上位ディレクトリの「foobar2000」と被ってしまうので途中で正規表現を使って「foobar2000 - 」の部分をカットしています。

呼び出すときに「/works/fb2k/install/foo.xml」等、ディレクトリ以外を引数にした場合、「foo.xml」は「/works/fb2k/install/」の構成ファイルの1つと見なし、上位ディレクトリは「/works/fb2k/」ではなく、同一ディレクトリの「/works/fb2k/install/」になります。1ページ1ディレクトリ方式に関しては『XMLベースのウェブサイトを構築する(1)』内で説明しています。

この関数自体は完全にこのサイト用にカスタマイズされているので他の環境で使用する場合は取得するファイルを「index.xml」ではなくて「index.php」などにする必要があるでしょう。

活用法

このサイトでは得られた配列$vdを以下のようにしてリストにしています。

$temp_hp = "<ol class=\"html_path\">\n";
foreach( $vd as $value ) {
    if ( $value['path'] ) {
        if( $value['subject'] == 'Arielworks.net' ) {
            $temp_hp .= '<li class="arielworks"><a href="' . $value['path'] . '" accesskey="'. ( count( $vdir ) - $value['depth'] ) . '">' . $value['subject'] . "</a></li>\n";
        } else {
            $temp_hp .= '<li><a href="' . $value['path'] . '" accesskey="'. ( count( $vdir ) - $value['depth'] ) .'">' . $value['subject'] . "</a></li>\n";
        }
    } else {
        if( $value['subject'] == 'Arielworks.net' ) {
            $temp_hp .= "<li class=\"arielworks\">" . $value['subject'] . "</li>\n";
        } else {
            $temp_hp .= "<li>" . $value['subject'] . "</li>\n";
        }
    }
}
$temp_hp .= "</ol>";
$html_path = $temp_hp;

get_vd()を実行した際、ファイルが存在しなかった場合はパスが空になってるはずなのでその時だけリンクしないようにしています。ルートディレクトリの「Arielwors.net」だけフォントを変えるために条件分岐していますが、これは他の環境では必要ないでしょう。アクセスキーは階層にあわせて1から順に振っておきます。

この後CSSで適当に整形します。Mozillaだけなら

ol.html_path li {
    display: inline;
}

ol.html_path li:first-child:before {
    content: "";
}

ol.html_path li:before {
    content: " -> ";
}

で問題有りませんがIE6が対応していないので

ol.html_path li {
    padding-left: 20px !i;
    margin-left: 3px !i;
    background-image: url( "./arrow" ) !i;
    background-repeat: no-repeat !i;
    background-position: 0% 50% !i;
}

とIE6のバグを使って適当に回避しておきます。背景画像に矢印の画像を指定して右側に表示される様にしておけば良いわけです。

連絡先、リンク、転載や複製などについては「サイト案内」をご覧ください。Powered by HIMMEL