PHPの質問です。


以下のようなテキストファイルがあります。
※「.」や「..」は階層の深さです

.|メニュー1|1.php
..|メニュー1-1|1-1.php
...|メニュー1-1-1|1-1-1.php
..|メニュー1-2|1-2.php
.|メニュー2|2.php
..|メニュー2-1|2-1.php
.|メニュー3|3.php

これを、最終的にHTMLへリストとして出力したいのです。

<ul id="menu">
<li><a href="1.php">メニュー1</a>
<ul>
<li><a href="1-1.php">メニュー1-1</a>
<ul>
<li><a href="1-1-1.php">メニュー1-1-1</a>
</ul>
<li><a href="1_2.php">メニュー1-2</a>
</ul>
<li><a href="2.php">メニュー2</a>
<ul>
<li><a href="2-1.php">メニュー2-1</a>
</ul>
<li><a href="3.php">メニュー3</a>
</ul>

いろいろと調べてやってみたのですが難しくて・・・
階層の深さは一定ではありません。
SEOを考えて、最終的にHTMLにリストとして出力できることが希望です。
これをPHPで実現できるでしょうか?

また、上記とは関係なく、効率よく、かつ簡単にPHPでツリーメニューを実現できるようなものもご紹介いただければ助かります。

回答の条件
  • 1人5回まで
  • 登録:2007/07/20 09:57:25
  • 終了:2007/07/20 14:42:04

回答(4件)

id:kn1967 No.1

kn1967回答回数2915ベストアンサー獲得回数3012007/07/20 10:38:14

ポイント10pt

phpファイルをサーバーにアップすると、そのサーバー内のファイルのリンクが一覧で表示できるツールを作成しています。 表示サンプル:http://dowdow.jp/sitemap.php URLの.. - 人力検索はてな

私が以前回答したものですがよろしければ改造してお使いください。

<?php 
//
//サイトマップ作成PHP
//
//----------定数宣言
    define("StartPath","./"); //探し始めるディレクトリ(末尾は/で終わっている事)
    define("MaxCall",2); //再帰呼び出しはメモリ(スタック)を食うので階層を決めておきます。
    define("ExtensionPattern","/\.(htm|html|php)$/"); //対象とするファイル拡張子
    define("SearchPattern","/<title>(.*)<\/title>/i"); //検索対象
//
//----------サブルーチン
    function dir_tree($path){
        static $count = 0; //この行は再帰呼び出し時には無視されますので階層が判ります。
        echo "<ul>$path\r\n";
        $count++; //ディレクトリ内に入る度に増分して現在深度を示す。
        if($count > MaxCall) {
            echo "<li>設定された階層深度限界のため、これ以下は表示されません</li>";
        }else{
            if(!is_dir($path)){ // $pathがディレクトリで無ければ何もしない。
            }elseif($handle = opendir($path)){
                // 
                // ディレクトリ内検索
                $index = false;
                $dir_array = array();
                $file_array = array();
                while(false !== $file = readdir($handle)){
                    if($file == "." || $file == "..") {
                    }elseif(is_dir($path . $file)) {
                        array_push($dir_array, $file);
                    }elseif($file == "index.html") {
                        $index = true;
                    }elseif(preg_match(ExtensionPattern,$file) == 1) {
                        array_push($file_array, $file);
                    }
                }
                closedir($handle);
                // 
                // ファイル名列挙
                sort($file_array); //ファイル名順にソート
                if($index == true){
                    unshift($file_array, "index.html"); //配列の先頭にindex.htmlを附加
                }
                foreach($file_array as $file){
                    if(preg_match(SearchPattern,join("",file($path . $file)),$match) == 1){
                        echo "<li><a href=\"$path$file\">$match[1]</a>\r\n";
                    }else{
                        echo "<li><a href=\"$path$file\">$file</a>\r\n";
                    }
                }
                // 
                // サブディレクトリ検索
                sort($dir_array); //ディレクトリ名順にソート
                foreach($dir_array as $dir){
                    dir_tree($path . $dir . "/");
                }
            }
        }
        $count--; //呼び出し元に戻るので深度を戻す。
        echo "</ul>\r\n";
    }
//
//----------メインルーチン
echo"<font size=\"2\">\r\n";
dir_tree(StartPath); //サイトマップを作成したい階層
echo"</font>\r\n";
?>
id:indiana

ありがとうございます。

ディレクトリはもともとツリー構造なので簡単に作れるのですが、質問の例の場合、テキストから読み込むので閉じタグ(</ul>)の処理などが難しいんです・・・

2007/07/20 11:09:08
id:tezcello No.2

tezcello回答回数459ベストアンサー獲得回数692007/07/20 11:16:07

ポイント10pt

こんなスクリプトでやってみました。

$str = <<<EOL
.|メニュー1|1.php
..|メニュー1-1|1-1.php
...|メニュー1-1-1|1-1-1.php
..|メニュー1-2|1-2.php
.|メニュー2|2.php
..|メニュー2-1|2-1.php
.|メニュー3|3.php

EOL;

$lines = preg_match_all('/(\.+)\|(.+)\|(.+\.php)/', $str, $arry);

$result = '<ul id="menu">'."\n";
$prev = 1;
for ($i=0; $i<$lines; $i++){
	$level = strlen($arry[1][$i]);
	if ($level > $prev){
		$result .= "<ul>\n";
		$prev = $level;
	}else if ($level < $prev){
		$result .= "</ul>\n";
		$prev = $level;
	}

	$result .= '<li><a href="'.$arry[3][$i].'">'.$arry[2][$i]."</a>\n";

}

$result .= "</ul>\n";

print $result;

基になるテキストファイルを読み込む代りに、ヒアドキュメントで代入しています。

実際の状況に合わせて、file_get_contents() に置換えるか、別の関数で作成するなどして下さい。

id:indiana

ありがとうございます。

試してみました。

しかし、「メニュー1-2」の後に「</ul>」が足りないため、意図した階層構造にはなりませんでした・・・

(お恥ずかしいのですが、質問文の方も足りてなかったですね・・・すみません)

2007/07/20 14:31:55
id:GEN111 No.3

GEN111回答回数472ベストアンサー獲得回数582007/07/20 11:40:41

ポイント100pt

ちょっと手抜き気味なので空行や

階層 | メニュー名 | URL

のフォーマットになっていない行があるとおかしな出力になります。

$str = <<< ENDTEXT
.|メニュー1|1.php
..|メニュー1-1|1-1.php
...|メニュー1-1-1|1-1-1.php
..|メニュー1-2|1-2.php
.|メニュー2|2.php
..|メニュー2-1|2-1.php
.|メニュー3|3.php
ENDTEXT;

function create_list($text) {
  $rv = '' ;
  $level = 1 ;
  foreach (explode("\n", chop($text)) as $one) {
    list($d, $l, $u) = explode('|', $one) ;
    $d = strlen($d) ;
    if ($d == $level && $rv != '') $rv .= "</li>\n" ;
    while ($d > $level) {
      ++$level ;
      $rv .= "\n<ul>\n" ;
    }
    while ($d < $level) {
      --$level ;
      $rv .= "</li>\n</ul>\n</li>\n" ;
    }
    $rv .= '<li><a href="'.$u.'">'.$l."</a>" ;
  }
  $rv .= "</li>\n" ;
  while ($level > 0) {
    --$level ;
    $rv .= "</ul>\n" ;
  }

  return '<ul id="menu">'."\n".$rv ;
}

echo create_list($str) ;

終了タグ </li> が不必要なら該当部分は削除してください。

id:indiana

ス・バ・ラ・シ・イ!

完璧です。

こんな短いコードでできたなんて・・・

目から鱗です。

ありがとうございました。

2007/07/20 14:38:06
id:Yota No.4

Yota回答回数453ベストアンサー獲得回数282007/07/20 12:03:50

ポイント10pt

http://www.wiki.cc/php/?title=HTML_TreeMenu

pearライブラリのHTML_TreeMenuなら、PHPコード内もしくはXMLファイルからツリーを作れます。

id:indiana

ありがとうございます。

試してみたのですが、元のXMLを書くなら直接HTMLでリストタグで書くのと手間が同じなので・・・

2007/07/20 14:39:58
  • id:kn1967
    既にテキスト化までは済んでいたのですね。早合点でした。。。
    ちょっと造ってみます。
  • id:kn1967
    // テキストファイル読み込み
    // 本当は $f=file(ファイル名); だけどこれはテスト用ということでarrayにしてます。
      $f = array("/menu/1/|メニュー1|1.php"
        ,"/menu/1//1|メニュー1-1|1-1.php"
        ,"/menu/1/1/1/|メニュー1-1-1|1-1-1.php"
        ,"/menu/1/2/|メニュー1-2|1-2.php"
        ,"/menu/2/|メニュー2|2.php"
        ,"/menu/2/1/|メニュー2-1|2-1.php"
        ,"/menu/3/|メニュー3|3.php"
      );
    // 現在の階層を示すカウンタをセット
      $c1 = 0;
    //
      foreach($f as $v){
        list($d,$t,$n) = preg_split("/\|/",$v,3);
        $c2 = preg_match_all("/\//",$d,$dummy);
        if($c2 > $c1){
    //     階層が深まった場合(必ず1階層ずつ深まる場合です。)
    //     一挙に深まることがある場合は浅くなった場合と同様にループ処理にしてください。
          echo "<ul>\r\n";
        }elseif($c2 < $c1){
    //     階層が浅くなった場合
    //     段差分だけループで処理します。
          for($lp=$c2;$lp<$c1;$lp++){
            echo "</ul>\r\n";
          }
        }
        echo "<li><a href=\"" . $d . $n . "\">" . $t . "</a></li>\r\n";
        $c1 = $c2;
      }
      for($lp=1;$lp<$c2;$lp++){
        echo "</ul>\r\n";
      }
    なお、このソースはコメント欄でも崩れないように行の先頭は全角スペースでインデントしてます。
    先頭のスペースやコメントは適宜削除してから使ってください。
  • id:indiana
    >kn1967さん
    わざわざありがとうございます。
    kn1967さんのものでもどうやらできそうですが、今回はテキストファイルのフォーマットが決まっているので。
    勉強のために参考にさせていただきます。

    でも、自分で組んでて「もしかしたらPHPでは不可能か?」と思っていたので助かりました。
  • id:kn1967
    .. や ... は本当にそのままの形だったのですね。
    (私がコメント欄に書いたスクリプトのように
    実際はディレクトリ名が入っているものとばかり思ってました)


    ファイル内の記述が..や...の場合は
        $c2 = preg_match_all("/\//",$d,$dummy);
    の行を
        $c2 = preg_match_all("/\./",$d,$dummy);
    に変えるだけで対応可能です。

    以上、蛇足ですが・・・

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

絞り込み :
はてなココの「ともだち」を表示します。
回答リクエストを送信したユーザーはいません