PHP: verzeichnisse rekursiv auslesen: Unverständliches code verhalten



  • hallo zusammen,

    ich habe zwar bereits den code dazu aber ich kann einfach nicht nachvollziehen, warum er korrekt funktioniert. ich komme grundsätzlich aus der C++ welt aber das macht hierbei nichts. anbei der code erstmal:

    public function ReadFiles($path)
    		{
    			$result = array();
    	      	$handle = opendir($path);
    
    			if($handle)
    			{
    				while(($file = readdir($handle)) !== false)
    	         	{
    					if($file != "." && $file != "..")
    	             	{
    	                	$name = $path . "/" . $file;
    	                	if(is_dir($name)) $this->ReadFiles($name);
    		                else $this->files[] =  $name;
    	                }
    				}
    			}
    			echo var_dump($this->files);
    		}
    

    was ich an gem code nicht verstehe, ist, warum wirklich alle verzeichnisse (egal wie viele in einem unterordner sind) ausgelesen werden und die darin enthaltenen dateien. Hier findet ja ein rekursives funktions verhalten statt, sobald $name ein ordner ist. das ist für mich soweit ja auch logisch. die funktion ruft sich selber wieder auf und übergibt dann als parameter das weitere unterverzeichnis, bei welchem es sich ja um ein verzeichnis handelt. bis hier ist es also logisch, so kann die funktion dann im nächsten unterverzeichnis weiter auslesen.

    und jetzt kommt die unlogik: warum werden aber dennoch alle unterverzeichnisse ausgelesen? diese wurden ja nie als parameter übergeben und da die funktion ja gleich beim ersten unterverzeichnis sich selbst wieder aufruft, wie können denn die anderen verzeichnisse die dort auch drin liegen miteinbezogen werden? das ergibt für mich einfach keinen sinn. beispiel:

    dir1
      udir1_1
      udir1_2
      udir1_3
    dir2
    dir3
      udir3_1
        uudir3_1
    

    hier würde beim ersten durchgang der funktion dir1 gleich als verzeichnis erkannt werden. also ruft die funktion sich selbst mit dem verzeichnis als parameter auf. soweit, sogut. jetzt aber wäre udir1_1 gleich der nächste selbstaufruf. hier wird es jetzt für mich unverständlich, denn: wie können nun alle anderen verzeichnisse noch miteinbezogen werden? also:

    udir1_2
    udir1_3
    
    und
    
    dir2
    dir3
      udir3_1
        uudir3_1
    

    versteht ihr?



  • Hallo,

    da fehlt es wohl an grundlegendem Verständnis. Die Funktion ist doch in dem Moment, wo sie sich selbst aufruft, nicht beendet!

    while(($file = readdir($handle)) !== false)
    

    ist die entscheidende Codestelle. Diese Schleife wird so lange aufgerufen, bis alle Dateien im aktuellen Verzeichnis gelesen wurden. Ist die aktuelle Datei dabei ein Verzeichnis, ruft sich die Funktion mit diesem Unterverzeichnis als Parameter selbst auf. Wenn nicht, oder falls dieser neue Funktionsaufruf beendet wurde, geht sie über zum nächsten Dateinamen.

    Zur Verdeutlichung:

    dir1 // ursprünglicher Funktionsaufruf, Funktion ruft sich selbst für dir1 auf.
      udir1_1 // dir1-Funktionsausruf, Funktion ruft sich selbst für udir1_1 auf.
         // udir1_1-Aufruf findet keine Verzeichnisse, ist somit beendet.
      udir1_2 // entsprechend mit udir1_2
      udir1_3 // entsprechend mit udir1_3.
      // Keine weiteren Verzeichnisse, dir1-Funktionsaufruf ist somit beendet.
    dir2 // urspr. Aufruf iteriert weiter (while-Schleife) zum nächsten Verzeichniss, das Ganze wiederholt sich.
    // usw. usf.
    


  • árn[y]ék schrieb:

    da fehlt es wohl an grundlegendem Verständnis. Die Funktion ist doch in dem Moment, wo sie sich selbst aufruft, nicht beendet!

    das ist mir schon klar, aber das erklärt mir immer noch nicht, wie die anderen verzeichnisse eingelesen werden können, die viel weiter vorne liegen, wie eben dir2 etc. für mich sieht das eben so aus: ruft die funktion sich selber auf, gibt es dann auch ein neues handle, demnach ist die while bedingung wiederrum von diesem neuen handle (also das unterverzeichnis) abhängig. das ergibt wirklich keinen sinn, kannst du mir das sonst bitte genauer beschreiben?

    irgendwie muss sich die funktion ja die verzeichnisse merken, die noch vor den unterverzeichnissen stehen, blos wo und wie? davon sehe ich einfach nichts. oder aber ist es so wie du eigentlich beschrieben hast (ich glaube ich verstehe es jetzt), dass die funktion sich zwar selber aufruft, aber erst dann wenn die momentane schleife beendet wurde? wenn ja, finde ich das ganze aber merkwürdig. meines erachtens ist dies bei C++ nicht der fall.

    ausserdem erklärt das aber auch nicht wie weitere unterverzeichnisse eingelesen werden, die nicht bei dir1 sind, sondern dir2, dir3. verflucht nochmals! 🕶 eine ausführliche erklärung wäre wirklich nett. mir ist nur nicht klar wann, wo und wie sich die funktion merkt, welche verzeichnisse noch relevant waren!



  • ich glaube jetzt verstehe ich es. wenn ich es richtig verstehe müsste die funktion sich mehrfach einfrieren, damit sie rekursiv auch weis wo es weiter ging. stimmt das nun?



  • Bei jedem Funktionsaufruf (egal ob rekursiv oder nicht) "merkt" sich die aufrufende Funktion ihren Zustand, so dass sie nach Ende der aufgerufenen Funktion einfach dort fortsetzt.

    Zudem ist readdir dafür verantwortlich, welche Verzeichnisse relativ zum übergebenen Handle bereits betrachtet wurden und welche noch nicht. Außerdem muss readdir IIRC Dateien nicht zwingend in alphabetischer Reihenfolge zurückgeben.

    Nicht vergessen, nach der Schleife das handle auch wieder zu schließen: http://de2.php.net/closedir



  • hallo,

    also findet kein mehrfaches einfrieren statt, sicher? weil ich dachte rekursive funktionen legen auf dem stack immer wieder einen neuen zustand ab. das hätte dann am meisten sinn gemacht. dann noch weitere fragen:

    1. das verhalten, das selbst nach dem selftaufruf, die funktion den unteren teil noch weiter ausführt, gibt es scheinbar nur bei PHP. bei C++ würde der untere teil nicht mehr ausgeführt werden, die funktion wird sofort selbst aufgerufen. warum ist das in PHP anderst?

    2. warum merkt es sich den zustand auch bei einem nicht rekursiven funktions-aufruf? auch dieses verhalten ist mir bei C++ nicht bekannt. nach einem aufruf verliert alles an gültigkeit, wenn es kein rekursiver aufruf war.



  • Irgendwie scheinen wir aneinander vorbeizureden. Um es kurz zu machen: PHP und C++ verhalten sich in dieser Sache ganz genau gleich.

    snorf schrieb:

    also findet kein mehrfaches einfrieren statt, sicher? weil ich dachte rekursive funktionen legen auf dem stack immer wieder einen neuen zustand ab. das hätte dann am meisten sinn gemacht.

    Genau das tun sie. Aber das hat nichts mit Rekursion zu tun, sondern wird prinzipiell bei jedem Funktionsaufruf getan.

    snorf schrieb:

    1. das verhalten, das selbst nach dem selftaufruf, die funktion den unteren teil noch weiter ausführt, gibt es scheinbar nur bei PHP. bei C++ würde der untere teil nicht mehr ausgeführt werden, die funktion wird sofort selbst aufgerufen. warum ist das in PHP anderst?

    Der untere Teil wird ausgeführt, sobald der rekursive Aufruf beendet ist.

    snorf schrieb:

    1. warum merkt es sich den zustand auch bei einem nicht rekursiven funktions-aufruf? auch dieses verhalten ist mir bei C++ nicht bekannt. nach einem aufruf verliert alles an gültigkeit, wenn es kein rekursiver aufruf war.

    Das wäre schlecht, oder?

    funktion1() {
        var irgendwas = 42;
        funktion2();
        // "irgendwas" soll es nicht mehr geben?
    }
    


  • Dasd schrieb:

    Genau das tun sie. Aber das hat nichts mit Rekursion zu tun, sondern wird prinzipiell bei jedem Funktionsaufruf getan.

    ja, aber nur um lokale variablen auf dem stack abzulegen, bzw. auf dem heap wenn speicher angefordert wird. aber nach dem verlassen eines nicht rekursiven aufrufs, verlieren alle variablen, selbst deklarierte zeiger, ihre gültigkeit. bei einem rekursiven verhalten ist das ja nicht ganz der fall.

    Dasd schrieb:

    Der untere Teil wird ausgeführt, sobald der rekursive Aufruf beendet ist.

    ich sehe aber wirklich ein unterschiedliches verhalten. wenn ich in einem C++ geschriebenem programm z.B. eine messagebox unter dem selbstaufruf aufrufen möchte, erscheint diese logischerweise nicht. versuche ich nun das gleiche mit PHP, ersetze natürlich den messagebox call durch ein echo, dann erhalte ich auch eine ausgabe. deswegen fiel mir der unterschied hierbei auch auf! 😮 🕶

    Dasd schrieb:

    Das wäre schlecht, oder?

    funktion1() {
        var irgendwas = 42;
        funktion2();
        // "irgendwas" soll es nicht mehr geben?
    }
    

    okay hier haben wir uns ein bisschen falsch verstanden. ich meinte das ja nicht so wie du das beispiel geschrieben hast. hier geht es ja um den gültigkeitsbereich des funktion1 rumpfes. dieser ist natürlich nach wie vor bestehend, sobald funktion2 aufgerufen wird. das ist natürlich auch in C++ so. ich meinte es vielmehr so, sobald funktion1 verlassen wird, hat in deinem beispiel die variable "irgendwas" keine gültigkeit mehr.

    demnach stiftet es für mich nach wie vor für verwirrung 😕 🙄



  • hm moment. ich glaub es stimmt ja alles. sowohl was ich sagte als auch du. einfrieren bedeutet für mich ja nichts anderes als eine neue umgebung der funktion. ich glaube ich weiss jetzt was mich verwirrt hat, nähmlich das die echo ausgabe einfach so da stand, dabei kommen die ja von jeder funktion einzeln. selbst bei C++ wäre dies dann mit der messagebox so, nur müsste man die erst mal weg klicken. dort habe ich es nur mit dem debugger getestet und das stiftete die verwirrung, weil der debugger natürlich immer an den gleichen codestellen war und ja nicht "zaubercode" für die neuen umgebungen geschrieben hat. einzig alleine dies hat eigentlich für die verwirrung gestiftet. demnach stimmt es also. jeder aufruf wird neu eingefrohren, also es wird eine neue umgebung geschaffen.

    sobald nun sagen wir der 12. aufruf beendet werden kann, er sich also nicht zum 13. mal aufrufen muss, erscheint seine echo anweisung als erstes, stimmt das? in anderen worten, die erste funktionsumgebung hat die letzte echo anweisung?



  • snorf schrieb:

    sobald nun sagen wir der 12. aufruf beendet werden kann, er sich also nicht zum 13. mal aufrufen muss, erscheint seine echo anweisung als erstes, stimmt das? in anderen worten, die erste funktionsumgebung hat die letzte echo anweisung?

    Ganz genau. Ich glaub' jetzt haben wir's. 🙂



  • super, vielen dank 👍 :xmas1:


Anmelden zum Antworten