preg_replace durch preg_replace_callback ersetzen


Bananenflanke

Grünschnabel
Hallo liebe User!

Bei unserer Website steht die Umstellung auf PHP 7 an - wird ja auch endlich Zeit. ;-)

Allerdings stoße ich dabei auf das Problem, dass ich die Funktion "preg_replace" (aufgrund /e) nicht erfolgreich ersetzen kann und hoffe, ihr könnt mir an dieser Stelle weiterhelfen.
Ich habe es bereits erfolglos mit verschiedenen Varianten zu preg_replace_callback und preg_replace_callback_array versucht.

PHP:
$patterns_anywhere = Array(
        '/\<!-- (.*?) -->/'=>'', // comment
        '/\&lt;br \/&gt;/'=>'<br />', // explicit line break
        '/\[\[Image:(.+?)\|([0-9]*)\|([0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2})\]\]/e'=>"showimage('\\1', '\\3', '\\2')", // specific image revision
        '/\[\[Image:(.+?)\|([0-9]+)\]\]/e'=>"showimage('\\1', '', '\\2')", // image with specific width
        '/\[\[Image:(.+?)\]\]/e'=>"showimage('\\1')", // image
        '/\[\[(:[^\|\]]*?)\|([^\]]+?)\]\]/e'=>"'<a href=\"' . wikilink('\\1') . '\">\\2</a>'", // link to category
        '/\[\[(:[^\|\]]*?)\]\]/e'=>"'<a href=\"' . wikilink('\\1') . '\">\\1</a>'", // link to category
        '/\[\[([^\|\]]+?)\|([^\]]+?)\]\]/e'=>"'<a title=\"%linkstart%\\1%linkend%\" href=\"' . wikilink('\\1') . '\"%class%>\\2</a>'", // link to page
        '/\[\[([^\|\]]+?)\]\]/e'=>"'<a href=\"' . wikilink('\\1') . '\"%class%>%linkstart%\\1%linkend%</a>'", // link to page
        '/\[([^\] ]+) ([^\]]+?)\]/'=>'<a href="\\1">\\2</a>', // external link
        '/\[([^\] ]+?)\]/'=>'<a href="\\1">\\1</a>', // external link
        "/&apos;&apos;&apos;&apos;&apos;(.+?)&apos;&apos;&apos;&apos;&apos;/"=>'<strong><i>\\1</i></strong>', // bold
        "/&apos;&apos;&apos;(.+?)&apos;&apos;&apos;/"=>'<strong>\\1</strong>', // bold
        "/&apos;&apos;(.+?)&apos;&apos;/"=>'<i>\\1</i>', // italic
        '/&lt;u&gt;(.+?)&lt;\/u&gt;/'=>'<u>\\1</u>', // underline
        '/&lt;s&gt;(.+?)&lt;\/s&gt;/'=>'<del>\\1</del>', // strike
        '/&lt;sub&gt;(.+?)&lt;\/sub&gt;/'=>'<sub>\\1</sub>', // subscript
        '/&lt;sup&gt;(.+?)&lt;\/sup&gt;/'=>'<sup>\\1</sup>' // superscript
);
// patterns which will be matched against whole lines
$patterns_lines = Array(
        '/^[\-]{4,}[\s]*$/'=>'<hr class="userline" />', // horizontal line
        '/^====== (.+?) ======[\s]*$/'=>'<h6>\\1</h6>', // heading level 6
        '/^===== (.+?) =====[\s]*$/'=>'<h5>\\1</h5>', // heading level 5
        '/^==== (.+?) ====[\s]*$/'=>'<h4>\\1</h4>', // heading level 4
        '/^=== (.+?) ===[\s]*$/e'=>"'<a id=\"' . make_anchor('\\1') . '\" name=\"' . make_anchor('\\1') . '\"></a><h3>\\1</h3>'", // heading level 3
        '/^== (.+?) ==[\s]*$/e'=>"'<a id=\"' . make_anchor('\\1') . '\" name=\"' . make_anchor('\\1') . '\"></a><h2>\\1</h2>'", // heading level 2
        '/^= (.+?) =[\s]*$/'=>'<h1>\\1</h1>', // heading level 1
);
    
//Code übersprungen

// regular formatting codes (defined in $patterns_anywhere)
foreach ($patterns_anywhere as $search=>$replace) {
    $string = preg_replace($search, $replace, $string, -1, $replace_count);z
    //Code übersprungen
}

//Code übersprungen

// regular formatting codes (defined in $patterns_lines)
$lines = explode("\n", $string);
foreach ($lines as $key=>$line) {
    foreach ($patterns_lines as $search=>$replace) {
        $line = preg_replace($search, $replace, $line);
    }
    $lines[$key] = $line;
}
Ich hoffe, ihr könnt mir weiterhelfen. Vielen Dank im Voraus!

Viele Grüße
Bananenflanke
 

Sempervivum

Erfahrenes Mitglied
Ich habe da etwas ausgearbeitet und hoffe, dass es dein Problem lösen kann:
PHP:
// diese Funktionen nur als Dummys für dein showImage()
function showImage1($a) {
    return 'showImage1 a=' . $a;
}
function showImage3($a, $b, $c) {
    return 'showImage3 a=' . $a . ' b=' . $b . ' c=' . $c;
}

// Erste Variante mit benannten Funktionen:
// function cb1($matches) {return showImage3($matches[1], '', $matches[2]);}
// function cb2($matches) {return showImage1($matches[1]);}
// $patterns_anywhere = [
//     '/\[\[Image:(.+?)\|([0-9]+)\]\]/' => "cb1",
//     '/\[\[Image:(.+?)\]\]/' => "cb2"
// ];

// Zweite Variante mit anonymen Funktionen:
$patterns_anywhere = [
    '/\[\[Image:(.+?)\|([0-9]+)\]\]/' => function ($matches) {return showImage3($matches[1], '', $matches[2]);},
    '/\[\[Image:(.+?)\]\]/' => function ($matches) {return showImage1($matches[1]);}
];

$subject2 = 'xrmhnt[[Image:abc|4711]]oiwhrj[[Image:def|4712]]himnrthotw[[Image:xyz]]';
foreach($patterns_anywhere as $patt => $cb) {
    $subject2 = preg_replace_callback($patt, $cb, $subject2);
}
echo $subject2;
Wie Du siehst, habe ich statt des Ersetzungsstrings jetzt die Callbackfunktionen in das Array eingetragen. Sicherlich kann man das noch auf preg_replace_callback_array() umstellen und das foreach einsparen.
Weil ich nicht wusste, wie deine Funktion showImage() aussieht und weil ich nicht in variable Parameter einsteigen wollte, habe ich zum Testen die zwei Dummyfunktionen showImage1() und showImage3() definiert.
 
Zuletzt bearbeitet:

Bananenflanke

Grünschnabel
Hallo Sempervivum,

vielen Dank für deine Mühe!
Wenn ich es richtig sehe, würde das aber nur das "showimage-Problem" beheben, oder?
Wie würde ich denn die neue Callback-Funktion für die restlichen Ersetzungen verwenden?
 

Sempervivum

Erfahrenes Mitglied
Ja, das sollte nur eine Demo sein, wie es im Prinzip funktioniert. Du wirst wohl nicht darum herum kommen, für jede Ersetzung eine eigene Callback-Funktion zu schreiben. Wenn Du erst Mal weißt, wie es funktioniert, ist es ja nur eine Fleißarbeit.
 

Bananenflanke

Grünschnabel
Dann habe ich das ja immerhin richtig verstanden. :)

Könntest du mir bitte noch ein Beispiel erstellen, wie es für eine Ersetzung funktionieren würde, die keine Funktion aufruft?
Also beispielswiese für '/^[\-]{4,}[\s]*$/'=>'<hr class="userline" />', // horizontal line
 

Sempervivum

Erfahrenes Mitglied
Gern. Dann müsste es wohl so aussehen:
Code:
$patterns_lines = Array(
    '/^[\-]{4,}[\s]*$/' => function($matches) {return '<hr class="userline" />';}
    // usw.
In dem Fall braucht man den Parameter $matches nicht, weil der gefundene String einfach ausgetauscht wird.
 

Bananenflanke

Grünschnabel
Ah, jetzt habe ich das Prinzip glaube ich verstanden.

Allerdings funktioniert das ja nicht, wenn es kein reines Austauschen ist, sondern noch Text dazwischen stehen soll.
Beispiel:
Code:
'/^====== (.+?) ======[\s]*$/'=> function($matches) {return '<h6>\\1</h6>';}, // heading level 6
Muss ich das dann in 2 Teile aufteilen oder gibt es da auch eine elegantere Lösung?
 

Sempervivum

Erfahrenes Mitglied
Dann steht dir der Text ja in $matches zur Verfügung. Sollte dann so funktionieren:
'/^====== (.+?) ======[\s]*$/'=> function($matches) {return '<h6>' . $matches[1] . '</h6>';},
 

Bananenflanke

Grünschnabel
Oh man, da stand ich ja total auf dem Schlauch. Sorry!

Dann arbeite ich das ganze mal ein und melde mich nochmal. Sieht soweit aber gut aus. Vielen Dank bis dahin!
 

Biki

Grünschnabel
Hallo zusammen,
da hier so wunderbar geholfen wird, trau ich mich mal - nachdem ich schon endlos probiert habe - mein Problem zu schildern.

Es wird dringend Zeit preg_replace mit dem Modifier e auszutauschen aber ich komme damit einfach nicht zurecht.
Wenn ich mehrer preg_replace_callback-Funktionen habe, muss ich dann für jedes Austauchen einen neuen Variablen-Namen nehmen oder kann die Funktion immer function ($m) heißen?

Hier ist mein Hauptproblem
PHP:
$stack[$p][$key]['PARSED'] = preg_replace('/(\{\s*(\w+)\((.*)\)\s*\})/e', '(method_exists(\'Template\', \'tpl_$2\'))?$this->tpl_$2("$3"):"$1"', $stack[$p][$key]['FUNC']);
Die Variablen $3 und $1 tauchen öfter auf und ich hab bei einer "leichteren" Stelle mal angefangen

PHP:
function tpl_calc($param)
    {
      $param =
        preg_replace('/{(\w+)}/e', '$this->getval("$1", true)',
        preg_replace('/\{('. PREG_FLT. ')\}/', '$1',
        preg_replace('/('. PREG_FLT. ')|\b\w+\b/', '{$0}', $param
      )));
      do
        $param = preg_replace(
          '/^\s*('. PREG_FLT. ')\s*([-\/\^|&!*+%]|&~|\|\||&&|!=)\s*('. PREG_FLT. ')/e',
          '$1$3$4', $tmp = $param);
      while ($tmp!=$param);
      return $param;
    } // Template::tpl_cal
Wenn ich das richtig verstanden habe muss das erste preg_replace folgendermaßen ersetzt werden.
PHP:
preg_replace_callback('/{(\w+)}/', function ($m) { return $this->getval($m[1], true);},
Muss die Variable $1 in der nächsten Zeile auch durch $m[1] ersetzt werden?
Bei der nächsten Umstellung bin ich mir schon gar nicht mehr sicher, was zu tun ist.
PHP:
preg_replace_callback(
          '/^\s*('. PREG_FLT. ')\s*([-\/\^|&!*+%]|&~|\|\||&&|!=)\s*('. PREG_FLT. ')/',
          function ($m) { return $m[1]$m[3]$m[4];}, $tmp = $param);
Vermutlich liege damit allerdings falsch.
Für Hilfe wäre ich echt dankbar.
Schöne Grüße
Biki
 

Sempervivum

Erfahrenes Mitglied
Wenn ich mehrer preg_replace_callback-Funktionen habe, muss ich dann für jedes Austauchen einen neuen Variablen-Namen nehmen oder kann die Funktion immer function ($m) heißen?
Die Funktion selber ist in dem Fall ja anonym und hat keinen Namen. Was Du meinst, ist der Name des Parameters $m und der kann jedes Mal gleich sein, die Funktion wird jedes Mal mit dem richtigen Wert für diesen Parameter aufgerufen.
 

Biki

Grünschnabel
Vielen Dank, Sempervivum.
Dann habe ich diese Frage schon einmal geklärt.
Kannst du mir sagen, ob mein Ersetzungsversuch richtig ist?
Ich habe viele Beispiele gefunden, wo z.B. ("\\1") in der Funktion durch $m[1] ersetzt wurde.
Hier habe ich $1 durch $m[1] ersetzt.
Schöne Grüße
Biki
 

Biki

Grünschnabel
Mittlerweile habe ich herausgefunden, dass es keinen Unterschied macht, ob ("$1") oder ("\\1") verwendet wird.
Wieder ein Problem weniger.

Außerdem denke ich, dass bei callback einige Zeichen (z.B. \ ) wegfallen müssen.
Alter Code:
PHP:
$stack[$p][$key]['PARSED'] = preg_replace('/(\{\s*(\w+)\((.*)\)\s*\})/e', '(method_exists(\'Template\', \'tpl_$2\')) ? $this->tpl_\\2("$3") : "$1"', $stack[$p][$key]['FUNC']);
Neu:
PHP:
$temp1 = 'Template';
$temp2 = 'tpl_$2';
$stack[$p][$key]['PARSED'] = preg_replace_callback('/(\{\s*(\w+)\((.*)\)\s*\})/', function($m){return(method_exists($temp1, $temp2)) ? $this->$temp2($m[3]) : $m[1];  }, $stack[$p][$key]['FUNC']);
Aber irgendwie funktioniert es trotzdem nicht.
Was mache ich denn da falsch?
 

Sempervivum

Erfahrenes Mitglied
Poste am besten mal einen Musterstring, den Du überarbeiten willst und beschreibe, was geändert werden soll.
 

Biki

Grünschnabel
Ja, gerne. Entschuldige, wenn das alles etwas konfus klingt, aber ich muss das irgendwie hinkriegen und habe nicht wirklich Ahnung davon.
Hier mal die gesamte Funktion
PHP:
      function parseTemplateString($tpl) {
          $parsedTpl = $tpl;
          $stack = array();
          $stackPointer = 0;
          $stackPointerStatus = array(TRUE);
          $tplPointer = 0;
          while ($tplPointer < strlen($tpl)) {
              $posOpen = strpos($tpl, '{', $tplPointer);
              $posClose = strpos($tpl, '}', $tplPointer);
              if ($posOpen == FALSE && $posClose == FALSE) {
                  $tplPointer = strlen($tpl);
              } elseif (($posOpen < $posClose) && $posOpen !== FALSE) {
                  if ($stackPointerStatus[$stackPointer] == TRUE) {
                      $stack[$stackPointer][] = array('START' => $posOpen);
                      $stackPointerStatus[$stackPointer] = FALSE;
                  } else {
                      $stackPointer++;
                      $stack[$stackPointer][] = array('START' => $posOpen);
                      $stackPointerStatus[$stackPointer] = FALSE;
                  }
                  $tplPointer = $posOpen + 1;
              } elseif (($posOpen > $posClose) || $posOpen == FALSE) {
                  $tplPointer = $posClose + 1;
                  $stackPointerStatus[$stackPointer] = TRUE;
                  $l = count($stack[$stackPointer]) - 1;
                  $stack[$stackPointer][$l]['END'] = $posClose;
                  $stackPointer--;
              }
          }
          for ($p = count($stack) - 1; $p >= 0; $p--) {
              if (is_array($stack[$p])) {
                  foreach ($stack[$p] as $key => $value) {
                      $stack[$p][$key]['FUNC'] = substr($tpl, $value['START'], ($value['END'] - $value['START'] + 1));
// replace functions
//--- UM DIESEN TEIL GEHT ES ---------------------------------------------------------------------------------
$stack[$p][$key]['PARSED'] = preg_replace('/(\{\s*(\w+)\((.*)\)\s*\})/e', '(method_exists(\'Template\', \'tpl_$2\')) ? $this->tpl_$2("$3") : "$1"', $stack[$p][$key]['FUNC']);
//--- UM DIESEN TEIL GEHT ES ---------------------------------------------------------------------------------
                  }
              }
          }
          #$tplReplacementOffset
          $replacePos = 0;
          for ($r = 0; $r < count($stack); $r++) {
              if (is_array($stack[$r])) {
                  foreach ($stack[$r] as $key => $value) {
                      $parsedTpl = str_replace($stack[$r][$key]['FUNC'], $stack[$r][$key]['PARSED'], $parsedTpl);
                  }
              }
          }
          $text = $parsedTpl;
          if (is_array($this->vars)) {
              foreach ($this->vars as $name => $value) {
                  if (($name != "content") || (strpos($text, '{'.$name.'}') !== false)) {
                      $text = str_replace('{' . $name . '}', $this->getval($name), $text);
                  }
              }
          }
          return $text;
      }
Den markierten Teil habe ich geändert
preg_replace --- preg_replace_callback
/e --- /
\'Template\' >>> 'Template'
\'tpl_$2\' >>> 'tpl_$2'
$this->tpl_$2("$3") >>> $temp2($m[3])
hier habe ich eine zusätzliche Variable erstellt ($temp2), da ich mit tpl_$2 und tpl_$m[2] eine Fehlermeldung erhielt
(Parse error: syntax error, unexpected '$'
Parse error: syntax error, unexpected '$m')
"$1" >>> $m[1]
PHP:
$temp2 = 'tpl_$2';
$stack[$p][$key]['PARSED'] = preg_replace_callback('/(\{\s*(\w+)\((.*)\)\s*\})/', function($m){return(method_exists('Template', 'tpl_$2')) ? $this->$temp2($m[3]) : $m[1];}, $stack[$p][$key]['FUNC']);
Kann es sein, dass die Seite erst dann wieder richtig angezeigt wird, wenn ich alle PREG_REPLACE_EVAL ersetzt habe?
Danke fürs Anschauen
Biki
 
Zuletzt bearbeitet:

Sempervivum

Erfahrenes Mitglied
Der ganze Code war gar nicht mal nötig. Was fehlt ist jedoch ein Beispielstring und eine Beschreibung, wie dieser geändert werden soll und wie er nachher aussehen soll.
 

Biki

Grünschnabel
Puh - ich habs raus gekriegt.
Aus
Code:
$stack[$p][$key]['PARSED'] = preg_replace('/(\{\s*(\w+)\((.*)\)\s*\})/e', '(method_exists(\'Template\', \'tpl_$2\')) ? $this->tpl_$2("$3") : "$1"', $stack[$p][$key]['FUNC']);
wird
Code:
 $stack[$p][$key]['PARSED'] = preg_replace_callback('/(\{\s*(\w+)\((.*)\)\s*\})/', function($m){$tpll = 'tpl_'.$m[2];return(method_exists('Template', 'tpl_'.$m[2])) ? $this->$tpll($m[3]) : $m[1];}, $stack[$p][$key]['FUNC']);
Danke für Denkanstöße, Sempervivum
 

Neue Beiträge