[PHP] recursive directory tree zonder array

Pagina: 1
Acties:
  • 275 views sinds 30-01-2008
  • Reageer

Onderwerpen


Acties:
  • 0 Henk 'm!

  • klaaz
  • Registratie: April 2000
  • Laatst online: 18-09 22:37

klaaz

it's me!

Topicstarter
Ik loop weer eens tegen de grenzen van mijn PHP capaciteiten aan :)

Ik probeer een script te bouwen welke een recursieve directory laat zien, en wel in de vorm van een tree. Voor de weergave van de tree gebruik ik een javascript (onvolprezen btw: dtree). Het ogenschijnlijk simpele probleem is dat ik een output moet genereren als dit:
code:
1
2
3
4
5
6
7
8
d.add(1,0, main directory1);
d.add(2,1, first subdir from main directory1);
d.add(3,1, second subdir from main directory1);
d.add(4,0, main directory2);
d.add(5,4, first subdir from main directory2);
d.add(6,4, second subdir from main directory2);
d.add(7,6, first subdir from second subdir frommain directory 2);
etc.


Het mag duidelijk zijn dat dtree aan de hand van de nummering de hierarchie vastlegt, warna het script een keurige tree laat zien.

Met wat ik tot dusver heb gebrouwen gaat dat ook prima. Zij het niet dat er geen sprake is van recursiviteit. In mijn script worden slechts 2 levels zichtbaar aangezien het script gewoon niet dieper gaat:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<div class="dtree">

<script type="text/javascript">
    d = new dTree('d');
    d.add(0,-1,'Hoofd directory');

<?
$dir = "testdata/";
if ($handle = opendir($dir)) 
    {
        $teller = 1;
        $basis  =   0;
        while (false !== ($file = readdir($handle))) 
            { 
                if ($file != "." && $file != "..")
                    {
                        if (!is_dir($dir.$file))
                            {   
                                echo "d.add($teller,$basis,'$file','#');\n";
                            }
                        else
                            {
                                $basis2     =   $teller;
                                echo "d.add($teller,$basis,'<strong>$file</strong>','#');\n";
                                $teller++;
                                
                                if ($handle2 = opendir($dir.$file."/")) 
                                    {
                                        while (false !== ($file2 = readdir($handle2))) 
                                            { 
                                                if ($file2 != "." && $file2 != "..")
                                                    {
                                                        echo "d.add($teller,$basis2,'$file2','#');\n";
                                                        $teller++;
                                                    }
                                            }   
                                         closedir($handle2); 
                                    }
                            }
                        $teller++;
                    }
            }
      closedir($handle); 
        echo "  document.write(d);";
    }

?>  

</script>
</div>


Ik heb uiteraard mijn script al de nodige keren getracht uit te breiden door o.a. te werken met functies. Maar geen van de pogingen is geslaagd. Voorbeelden en tutorials op het net werken vrijwel allemaal met PHP Arrays, maar ik wil vanwege de goede ervaring graag gebruik maken van dtree.

Na een dag zoeken, sleutel en teleurstellingen ben ik redelijk gefrustreerd... |:(

Wie geeft me een zetje in de rug... ;)

Acties:
  • 0 Henk 'm!

  • Technicality
  • Registratie: Juni 2004
  • Laatst online: 13:07

Technicality

Vliegt rechtsom...

Je kan toch een php array gewoon op die manier uitvoeren? Waarom lukt dat niet?

edit: ik vind overigens dat dtree niet zo héél goed; het zou semantisch correcter zijn als je de tree in html zou weergeven als geneste unordered lists (<ul>).

[ Voor 50% gewijzigd door Technicality op 28-01-2007 03:13 ]


Acties:
  • 0 Henk 'm!

  • CyBeR
  • Registratie: September 2001
  • Niet online

CyBeR

💩

Dit is ook helemaal niet recursief. Je doet gewoon een opendir() en als een van de resulterende files een dir is doe je daar ook een opendir() op. Dan kun je inderdaad maar twee levels diep.

Wat je moet doen is een volgende soort constructie:

code:
1
2
3
4
5
6
7
8
9
10
11
12
functie recursive_dir($dir){
  $bla = opendir($dir);
  

  while (($contents = readdir($bla))){
    if (is_dir($contents)){
      recursive_dir($contents);
    } else {
      echo "d.add(blablabla";
    }
  }
}


(Voor de duidelijkheid da's geen echte code :P)

M.a.w. je hebt dus een functie die zichzelf aanroept. Dat is recursive programmeren.

[ Voor 4% gewijzigd door CyBeR op 28-01-2007 03:22 ]

All my posts are provided as-is. They come with NO WARRANTY at all.


Acties:
  • 0 Henk 'm!

Verwijderd

@TS:

Je bent er al bijna, je hoeft alleen de hoofdfunctionaliteit in een functie te stoppen en deze functie zichzelf te laten aanroepen. Gebruik daarbij als parameters in ieder geval de handle van de folder die je wil doorlopen.

Als je een nummer hebt wat vast doorloopt kun je deze ook als parameter meenemen, maar je kan ook een statische variabele gebruiken (kijk naar static in de manual).

Let ook op dat je even een limiet toevoegt aan het aantal lagen, iemand zal maar (per ongeluk) een loop hebben gemaakt (via symbolische links bijvoorbeeld) dan loopt je script vast.

[ Voor 75% gewijzigd door Verwijderd op 28-01-2007 05:23 ]


Acties:
  • 0 Henk 'm!

  • mschol
  • Registratie: November 2002
  • Niet online
hier ben ik ook wel benieuwd naar,
@ts zou je de code die werkt met ons kunnen delen ?

Acties:
  • 0 Henk 'm!

  • Farmerwood
  • Registratie: September 2004
  • Niet online
Heb even een klein voorbeeldje in elkaar geflanst dat recursief een directorylisting opvraagt.
Je zult er nog wel aan moeten sleutelen voor een nette weergave, maar het idee van recursief programmeren maakt het wel duidelijk.

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php

function dirList( $dirToList )
{

        if( $dir = @opendir($dirToList ) )
        {
                while( ($file = readdir($dir)) !== false )
                {
                        if( $file != "." && $file != ".." )
                        {
                                if( is_dir( $dirToList . "/" . $file) )
                                {
                                        dirList( $dirToList . "/" . $file );
                                }
                                else
                                {
                                        echo( $dirToList . "/" . $file . "<br>");
                                }
                        }
                }

                closedir($dir);
        }
}

echo("Directory Listing Example:<br><br>");

dirList( "/home/sjoerd" );

?>


Je roept de functie aan met de rootdir vanaf waar je de listing wilt. Ik heb hier mijn homedir genomen.
De . en .. filter je eruit, want die wil je niet mee listen. Vervolgens bekijk je per item of het een file of dir is. Zodra het een directory blijkt te zijn roep je dezelfde functie weer aan, maar dan met deze nieuwe directorynaam. Die aanroep zorgt ervoor dat die hele subdir en eventuele daar onderliggende dirs gelist worden. Vervolgens kom je weer terug in deze eerste aanroep en ga je verder met het volgende item.

Als recursief je nog niet helemaal duidelijk is: http://nl.wikipedia.org/w...ecursie_in_de_informatica

Acties:
  • 0 Henk 'm!

  • klaaz
  • Registratie: April 2000
  • Laatst online: 18-09 22:37

klaaz

it's me!

Topicstarter
ok, ben net weer in het land der levenden en ga weer aan de slag. En @mstoel natuurlijk wil het delen als het klaar is :)

@TRRoads: ik kijk naar static, want ik heb wel constructies gebruikt waarin ik de functie opnieuw aanriep met de nummering als variabele maar om de een of andere reden werd de nummering weer gereset...

En btw, ik vondt dat aanroepen van een functie binnen een functie zelf erg dirty, maar het is me nu duidelijk dat dit een optie is, geen bug 8)7

Acties:
  • 0 Henk 'm!

  • CyBeR
  • Registratie: September 2001
  • Niet online

CyBeR

💩

klaaz schreef op zondag 28 januari 2007 @ 12:10:
En btw, ik vondt dat aanroepen van een functie binnen een functie zelf erg dirty, maar het is me nu duidelijk dat dit een optie is, geen bug 8)7
Daar is niks dirty aan, dat heet recursief :)

All my posts are provided as-is. They come with NO WARRANTY at all.


Acties:
  • 0 Henk 'm!

  • klaaz
  • Registratie: April 2000
  • Laatst online: 18-09 22:37

klaaz

it's me!

Topicstarter
Ok, ik ben dankzij jullie hulp weer een stapje verder gekomen. Ik heb nu een keurige weergave van de tree. Alleen... in de hoofddirectory staat een bestand, deze wordt echter niet in de hoofddirectory weergegeven. De bestanden in de subdirectories worden wel netjes geplaatst.

Even een plaatje (ik heb een testmap + testdirectories en testbestanden gemaakt ):
Afbeeldingslocatie: http://www.avianoord.nl/klaaz/tree_voorbeeld.jpg

Het rood omcirkelde testbestandje staat dus in de hoofdmap op het eerste level. Op de een of andere manier lukt het me niet om deze de goede referentie mee te geven zodat deze op de juiste plaats wordt weergegeven.

Mijn huidige code (compleet)
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
<link rel="StyleSheet" href="includes/dtree/dtree.css" type="text/css" />
<script type="text/javascript" src="includes/dtree/dtree.js"></script>


    <p><a href="javascript: d.openAll();">open all</a> | <a href="javascript: d.closeAll();">close all</a></p>
<br /><br />
<div style="width:600px;height:600px;overflow:auto;border:1px ridge silver;padding:10px;"
<div class="dtree">

<?php
function read_dir($dir) 
    {
        static $basis = 0;
        static $teller = 1;
        $path = opendir($dir);
        while (false !== ($file = readdir($path))) 
            {
        if($file!="." && $file!="..") 
                    {
            if(is_file($dir."/".$file))
                            {
                    $files[]=$file;
                            }
            else 
                            {
                    $dirs[]=$dir."/".$file;
                            }
            }
        }
            
        if($dirs) 
            {
        natcasesort($dirs);
                $basis2     =   $teller;
                foreach($dirs as $dir) 
                    {
                        echo "d.add($teller,".($basis2-1).",'".substr($dir, strrpos($dir, "/")+1)."','#');\n";
                        //echo "<span style=\"color:red;\">$teller - ".($basis2-1)." ".$dir."</span><br />";
                        $teller++;
            read_dir($dir);
            }
        }
   
        if($files) 
            {
                natcasesort($files);
                $basis      =   $teller;
                foreach ($files as $file) 
                    {
                        echo "d.add($teller,".($basis-1).",'$file','#');\n";
                        //echo "&nbsp;&nbsp;$teller - ".($basis2-1)." ".$file."<br />";
                        $teller++;
                    }
            }
        closedir($path);
    }


$path = "testdata";
//echo read_dir($path);

?>
<script type="text/javascript">
    d = new dTree('d');
    d.add(0,-1,'Overview');
    <? echo read_dir($path);?>
    document.write(d);
</script>

</div>
</div>

Het spreekt voor zich dat je dtree in een map "includes/" moet hebben staan om de tree goed weer te geven. Je kunt echter de regels 38, 51 en 60 uncommenten, en de regels 37, 50 en 59 commenten om zonder dtree een idee te krijgen wat er mis gaat.

Verder heb ik nog wat issue's met paden en bestandsnamen waar vreemde tekens in staan (in de live situatie) maar dat ga ik nog wel oplossen denk ik.

Ziet iemand door de bomen het bos nog? :D

Acties:
  • 0 Henk 'm!

  • klaaz
  • Registratie: April 2000
  • Laatst online: 18-09 22:37

klaaz

it's me!

Topicstarter
Ik ben er ondertussen wel achter dat het natuurlijk met de volgorde van inlezen te maken heeft en dat het betreffende bestand als laatste wordt ingelezen. Ik heb echter alles geprobeerd en krijg het niet voor elkaar om het bestand in de hoofdmap ook daadwerkelijk in de hoofdmap weer te geven... :(

Volgens mij denk ik gewoon verkeerd, maar ik zie het niet echt meer ... Wie geeft mij de gouden tip?

Acties:
  • 0 Henk 'm!

  • mithras
  • Registratie: Maart 2003
  • Niet online
Je kan spelen met de functie zoals Farmerwood in "[PHP] recursive directory tree zonder ar...", die eigenlijk geript is van een reactie op http://php.net/opendir website.

Ikzelf heb eens een recursieve copy functie gemaakt die ook hierop is gebaseed, welke wel wat uitleg behoeft:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
     * Recursive function to copy data from one folder to another
     * Function adds html code to the display string
     * Credits by Jurian Sluiman: http://juriansluiman.nl
     * @param string $old_location
     * @param string $new_location
     * @param bool $intern
     * @return bool
     */
function copy_recursive($old_location, $new_location, $intern=false){
    if (is_dir($old_location)) {
        if ($dh = opendir($old_location)) {
            while (($file = readdir($dh)) !== false ) {
                if( $file != "." && $file != ".." ){
                    if( is_dir( $old_location.$file ) ){
                        if(!file_exists($new_location.$file)){
                            $this->display .= $this->indention."Maak de map $file aan<br>";
                            mkdir($new_location.$file."/");
                            chmod($new_location.$file."/",0775);
                        }
                        $this->display .= $this->indention."De map $file wordt binnen gegaan<br>";
                        $this->indention = $this->indention."&nbsp;&nbsp;&nbsp;";
                        if($this->copy_recursive($old_location.$file."/", $new_location.$file."/", true)){
                            $return = true;
                        }
                        $this->indention = substr($this->indention,0,-18);
                    }else{
                        $this->display .= $this->indention."Kopi&euml;er $file...";
                        if(copy($old_location.$file,$new_location.$file)){
                            chmod($new_location.$file,0774);
                            $this->display .= "Ok!<br>";
                            $return = true;
                        }
                    }
                    if(!$return){
                        return false;
                    }
                }
            }
            closedir($dh);
            return true;
        }
    }
}
De functie behoort binnen een klasse ("bar" in het voorbeeld) te zitten. Op een volgende manier kan je de functie gebruiken:
PHP:
1
2
3
4
5
6
$foo = new bar();
if ($foo->copy_recursive($base_folder, $destination_folder) ){
  echo "<pre>".$foo->display."</pre>";
}else{
  echo "Er ging iets mis met het kopieren";
}
De functie retourneert dus een boolean wanneer het kopieren geslaagd is. Als er een fout optreedt wordt gelijk false geretourneerd. In de variabele $foo->display wordt de html output opgeslagen.

Ik neem aan dat je ipv. Copy wel wat anders kan verzinnen om het passend voor jouw situatie te maken. In ieder geval moet je rekening houden met je diepte waarin je zit, wat ik doe met $this->indention. Zo zorg je dat 'file in Overview map.css' wél op de juiste plek terecht komt ;)
Pagina: 1