Daniel (nun registriert): \\1 von preg_replace() an eigene Funktion übergeben

Hallo,

es geht um folgenden Code:

<?php  
  
function parse_file($file)  
{  
echo"<p>function parse_file: übergebene Datei: $file</p>";  
if(empty($file) || !file_exists($file)) { echo"<p>aborted - file $file</p>"; return; }  
$handle = fopen ($file, "r");  
$filecontent = fread ($handle, filesize ($file));  
fclose ($handle);  
  
$filecontent=preg_replace("/\{file:([[:alnum:]]+\.tpl)\}/", parse_file("\\1"), $filecontent);  
  
return $filecontent;  
}  
  
  
function test($var)  
{  
$var="<p>function test: übergebene datei: <em>$var</em></p>";  
return $var;  
}  
  
  
echo parse_file("test.tpl");  
  
?>

Die test.tpl:
Dies ist test.tpl, enthält den Verweis auf test2.tpl
{file:test2.tpl}

Die test2.tpl:
<em>Dies ist test2.tpl</em>

Führe ich das ganze aus, erhalte ich folgende Ausgabe:
function parse_file: übergebene Datei: test.tpl
function parse_file: übergebene Datei: \1
aborted - file \1
Dies ist test.tpl, enthält den Verweis auf test2.tpl

Die 2. und 3. Zeile bzw. eben deren Angabe für $file machen keinen Sinn.

Beachten wir folgende Zeile:
$filecontent=preg_replace("/\{file:([[:alnum:]]+\.tpl)\}/", parse_file("\\1"), $filecontent);
Ok, evtl. etwas falsch an parse_file("\1") dachte ich mir, aber dann habe ich parse_file("\1") durch test("\1") ersetzt – der einzige Unterschied ist der Funktionsname (d.h. keine der beiden funktion ändert die übergebene Variable vor der Ausgabe ab!), aber hier erhalte ich nun eine sinnvolle Ausgabe für $file. Die gesamte Ausgabe (parse_file("\1") durch test("\1") ersetzt):
function parse_file: übergebene datei: test.tpl
Dies ist test.tpl, enthält den Verweis auf test2.tpl
function test: übergebene datei: test2.tpl

Ich habe jetzt schon ne ganze weile gesucht, aber ich verstehe einfach nicht, was ich falsch mache?

Vielen Dank schonmal für eure Hilfe!

  1. Hi Daniel,

    $filecontent=preg_replace("/{file:([[:alnum:]]+.tpl)}/", parse_file("\1"), $filecontent);

    So wie ich die PHP-Doku  verstehe, muss da

    parse_file($1) hin, nicht parse_file("\1").

    Gruesse,
    Joerg

    1. parse_file($1) hin, nicht parse_file("\1").

      Danke für deine Antwort, leider funktioniert es so nicht:
      Parse error: parse error, unexpected T_LNUMBER, expecting T_VARIABLE or '$' in func.php on line 11 (die zeile mit preg_replace).

      Das einzige was ich hinbekommen habe, ist parse_file(test2.tpl) auszugeben, aber eben als string, nicht den rückgabewert der entsprechenden funktion.
      so rein aus neugier habe ich es mal mit
      call_user_func(parse_file, "$1") statt parse_file($1)
      probiert, dann erhalte ich als ausgabe:

      function parse_file: übergebene datei: test.tpl
      function parse_file: übergebene datei: $1
      aborted - file $1
      Dies ist test.tpl, enthält den Verweis auf test2.tpl

      Mit call_user_func(test, "$1") erhalte ich folgende ausgabe:
      function parse_file: übergebene datei: test.tpl
      Dies ist test.tpl, enthält den Verweis auf test2.tpl
      function test: übergebene datei: test2.tpl

      ->Alles richtig. Ich verstehe nicht, warum die übergabe an parse_file() nicht funktioniert ):
      Was soll da bitte anders sein?

  2. Habe das Problem nun gelöst bzw. ne Lösung im Netz gefunden, benötigt wird der sogenannte Modifier e, siehe auch
    http://regexp-evaluator.de/tutorial/php-funktionen/preg-replace/ (unter preg_replace() und Modifier e)
    http://de.php.net/manual/de/function.preg-replace-callback.php#44863
    (funktionierendes Beispiel).

    Mein funktionierender Code sieht nun so aus:
    $filecontent=preg_replace("/{file:([[:alnum:]]+.tpl)}/e", "parse_file('\1')", $filecontent);

    Was mir immer noch ein Rätsel bleibt ist, warum es mit der Funktion test() ohne Probleme geklappt hat, die hätte eigentlich auch /e benötigt, zumindest nach meinem Verständnis. Weiß jmd. warum es in diesem Fall ohne funktionierte?

    1. Servus,

      Mein funktionierender Code sieht nun so aus:
      $filecontent=preg_replace("/{file:([[:alnum:]]+.tpl)}/e",
      "parse_file('\1')", $filecontent);

      besser wäre es wohl, gleich preg_replace_callback() zu benutzen; mich
      erstaunt aber eher, dass der POSIX Ausdruck [[:alnum:]] tatsächlich mit
      preg_replace funktionieren soll.

      Was mir immer noch ein Rätsel bleibt ist, warum es mit der Funktion
      test() ohne Probleme geklappt hat, die hätte eigentlich auch /e
      benötigt, zumindest nach meinem Verständnis. Weiß jmd. warum es in
      diesem Fall ohne funktionierte?

      Kann ich mir eigentlich nicht vorstellen - ohne den Eval-Paramater,
      wird das Ersatzmuster immer als String behandelt.

      Grüsse

    2. Moin!

      Was mir immer noch ein Rätsel bleibt ist, warum es mit der Funktion test() ohne Probleme geklappt hat, die hätte eigentlich auch /e benötigt, zumindest nach meinem Verständnis. Weiß jmd. warum es in diesem Fall ohne funktionierte?

      Ist recht simpel: In deiner Version wird zuerst die Funktion aufgerufen (parse_file oder test), mit dem Parameterstring "\1" als Argument. Der Rückgabewert wird dann als Ersetzung in preg_replace genutzt. Und in dieser Ersetzung steckt dann \1 drin und wird durch den Dateinamen ersetzt.

      Deine Funktion test gibt den String "\1" im Funktionsergebnis zurück.

      Da aber deine parse_file-Funktion mit "\1" als Dateinamen nichts anfangen kann, und dann nur einen Leerstring zurückgibt, wird die Include-Anweisung dann eben durch einen Leerstring ersetzt, nicht durch den Dateinamen oder durch deren Inhalt.

      - Sven Rautenberg

      --
      "Love your nation - respect the others."
  3. Hallo Daniel,

      
    
    > $filecontent=preg_replace("/\{file:([[:alnum:]]+\.tpl)\}/", parse_file("\\1"), $filecontent);  
    
    

    Wie Dir Jörg bereits mitteilte, kannst Du der Dokumentation entnehmen, dass Du statt den doppelten Backslashes bei Ersatz die Dollar-Variante nehmen solltest:

    <zitat>
        Ersatz darf Referenzen in der Form \n oder (seit PHP 4.0.4) $n enthalten, wobei Letztere vorzuziehen ist.
    </zitat>

    Weiterhin hast Du ein kleines Problemchen mit Deinen Anführungszeichen, Jörg hatte dies übersehen:

    <zitat>
        mixed preg_replace ( mixed Suchmuster, mixed Ersatz, mixed Zeichenkette [, int Limit [, int &Anzahl]] )
    </zitat>

    Ersatz ist also vom Typ mixed, entweder eine Zeichenkette oder ein Array von Zeichenketten. Dir genügt eine Zeichenkette. Die einfache Variante ist diese:

      
    $filecontent=preg_replace("/\{file:([[:alnum:]]+\.tpl)\}/", parse_file('$1'), $filecontent);  
    
    

    Die einfachen Anführungszeichen sind wichtig!

    Die test.tpl:
    Dies ist test.tpl, enthält den Verweis auf test2.tpl
    {file:test2.tpl}

    Die test2.tpl:
    <em>Dies ist test2.tpl</em>

    Führe ich das ganze aus, erhalte ich folgende Ausgabe:
    function parse_file: übergebene Datei: test.tpl
    function parse_file: übergebene Datei: \1
    aborted - file \1

    ja, ja, die doppelten Anführungszeichen schlagen zu. In doppelten Anführungszeichen ist der Backslash ein Escape-Zeichen. Es maskiert das folgende Zeichen, in Deinem Fall der Backslash, d.h. aus "\1" wird die Zeichenfolge

    \1

    Das ist keine Backreference :-) In doppelten Anführungszeichen und mit der veralteten Backreference mit doppelten Backslashes müsstest Du also

    "\\1"

    schreiben, um

    \1

    zu erhalten. Viel einfacher wäre jedoch die Variante

    '\1'.

    Am besten ist jedoch

    '$1',

    was viel besser ist als

    "$1",

    was auch funktionieren sollte, da dies ebenfalls

    $1

    zurückliefert im Gegensatz zu

    "$1".

    Gewöhne Dir an, nach Möglichkeit einfache Anführungszeichen zu verwenden. Diese sind sehr angenehm und dazu noch performant.

    Freundliche Grüße

    Vinzenz

    1. echo $begrüßung;

      In doppelten Anführungszeichen [...] müsstest Du also

      "\\1"

      schreiben, um

      \1

      zu erhalten. Viel einfacher wäre jedoch die Variante

      '\1'.

      Hier irrt der Meister. Auch in einfachen Anführungszeichen wirkt der \ als Escape-Zeichen. Anders als in doppelten Anführungszeichen wirkt er jedoch nur bei zwei Zeichen. Das eine ist das einfache Anführungszeichen, das zweite der Backslash. '\1' ergibt also auch nur \1.

      echo "$verabschiedung $name";

      1. Hallo dedlfix,

        Hier irrt der Meister.

        Du hast völlig recht. Am Ende einer langen Woche sollte man nur das posten, was man durchgetestet hat. Danke für die Korrektur.

        Freundliche Grüße

        Vinzenz