0

We have got web app which does replacing some text with another using str_replace(). Find strings and replace strings are stored in template file.

We what to replace str_replace() function to preg_replace() to have possibility to use regex in find strings (to set them in the same template file).

In original scripts we have such parts of php code.

In one file:

class SiteConfig {

// Strings to search for in HTML before processing begins (used with $replace_string)
public $find_string = array();
// Strings to replace those found in $find_string before HTML processing begins
public $replace_string = array();

// a lot of code goes here

public function append(SiteConfig $newconfig) {
    foreach (array('find_string', 'replace_string') as $var) {
        // append array elements for this config variable from $newconfig to this config
        //$this->$var = $this->$var + $newconfig->$var;
        $this->$var = array_merge($this->$var, $newconfig->$var);
    }
}

// a lot of code goes here

public static function build_from_array(array $lines) {
    $config = new SiteConfig();
    foreach ($lines as $line) {
        $line = trim($line);

        // skip comments, empty lines
        if ($line == '' || $line[0] == '#') continue;

        // get command
        $command = explode(':', $line, 2);
        // if there's no colon ':', skip this line
        if (count($command) != 2) continue;
        $val = trim($command[1]);
        $command = trim($command[0]);
        //if ($command == '' || $val == '') continue;
        // $val can be empty, e.g. replace_string: 
        if ($command == '') continue;

        // strip_attr is now an alias for strip.
        // In FTR 3.8 we can strip attributes from elements, not only the elements themselves
        // e.g. strip: //img/@srcset (removes srcset attribute from all img elements)
        // but for backward compatibility (to avoid errors with new config files + old version of FTR)
        // we've introduced strip_attr and we'll recommend using that in our public site config rep.
        // strip_attr: //img/@srcset
        if ($command == 'strip_attr') $command = 'strip';

        // check for commands where we accept multiple statements
        if (in_array($command, array('title', 'body', 'author', 'date', 'strip', 'strip_id_or_class', 'strip_image_src', 'single_page_link', 'single_page_link_in_feed', 'next_page_link', 'native_ad_clue', 'http_header', 'test_url', 'find_string', 'replace_string'))) {
            array_push($config->$command, $val);
        // check for single statement commands that evaluate to true or false
        } elseif (in_array($command, array('tidy', 'prune', 'autodetect_on_failure', 'insert_detected_image'))) {
            $config->$command = ($val == 'yes');
        // check for single statement commands stored as strings
        } elseif (in_array($command, array('parser'))) {
            $config->$command = $val;
        // special treatment for test_contains
        } elseif (in_array($command, array('test_contains'))) {
            $config->add_test_contains($val);
        // special treatment for if_page_contains
        } elseif (in_array($command, array('if_page_contains'))) {
            $config->add_if_page_contains_condition($val);
        // check for replace_string(find): replace
        } elseif ((substr($command, -1) == ')') && preg_match('!^([a-z0-9_]+)\((.*?)\)$!i', $command, $match)) {
            if (in_array($match[1], array('replace_string'))) {
                array_push($config->find_string, $match[2]);
                array_push($config->replace_string, $val);
            } elseif (in_array($match[1], array('http_header'))) {
                $_header = strtolower(trim($match[2]));
                $config->http_header[$_header] = $val;
            }
        }
    }
    return $config;
}

}

In another file:

public function process($html, $url, $smart_tidy=true) {

    // a lot of code goes before

    // do string replacements
    if (!empty($this->config->find_string)) {
        if (count($this->config->find_string) == count($this->config->replace_string)) {
            $html = str_replace($this->config->find_string, $this->config->replace_string, $html, $_count);
            $this->debug("Strings replaced: $_count (find_string and/or replace_string)");
        } else {
            $this->debug('Skipped string replacement - incorrect number of find-replace strings in site config');
        }
        unset($_count);
    }

    // a lot of code goes after
}

I tried to replace str_replace() with preg_replace(), but while testing it shows an error:

Warning: preg_replace(): No ending matching delimiter '>' found in this line:

            $html = preg_replace($this->config->find_string, $this->config->replace_string, $html, $_count);

Where is the error and how to replace str_replace() function to preg_replace() correctly? I'm very very beginning in php, so any help is badly needed. Big thanks in advance!

Jesty
  • 5
  • 4
  • With that kind of error, we're going to need to see how you're constructing your regular expression string. `preg_replace` is not a "drop in" replacement for `str_replace`, at minimum you may need `'/'.preg_quote($this->config->find_string).'/'` for non regex based patterns, but then that's just silly and has no benefit over the former. – Scuzzy Jul 29 '18 at 21:06
  • This error goes even I have no any regular expression string in template. – Jesty Jul 29 '18 at 21:11
  • regex in php expects a leading and trailing character as part of the pattern, eg to match `hello` your expression would be `/hello/` http://php.net/manual/en/regexp.reference.delimiters.php – Scuzzy Jul 29 '18 at 21:11
  • Yes, of cause, when I tried to set regular expression in template it was: find_string: /teststing/ – Jesty Jul 29 '18 at 21:14
  • You're going to have to us show what's in `$this->config->find_string` and `$this->config->replace_string` i'd say. – Scuzzy Jul 29 '18 at 21:16
  • But even when I remove string 'find_string: /teststing/' from template file it shows the same error, so I think that the error in this string construction '$html = preg_replace($this->config->find_string, $this->config->replace_string, $html, $_count);' – Jesty Jul 29 '18 at 21:19
  • `Warning: preg_replace(): No ending matching delimiter '>' found in this line:` this error is telling us there's a problem with a particular regular expression, show us it and we can help you. – Scuzzy Jul 29 '18 at 21:19
  • Possible duplicate of [PHP regular expressions: No ending delimiter '^' found in](https://stackoverflow.com/questions/4634993/php-regular-expressions-no-ending-delimiter-found-in) – Scuzzy Jul 29 '18 at 21:20
  • @Scuzzy, when I delete whole regex from settings, the same error persist, so it is not about my regex. – Jesty Jul 29 '18 at 21:24
  • show us the content of `$this->config->find_string` and `$this->config->replace_string` please – Scuzzy Jul 29 '18 at 21:28
  • @Scuzzy Using echo? `echo $this->config->find_string;` or how to see that content? – Jesty Jul 29 '18 at 21:34
  • With `str_replace()` (as it was in original script) everything is working fine. I just need to set `find_string: teststingtofind` in template file. – Jesty Jul 29 '18 at 21:51
  • You can use `var_dump()` or `print_r()` to display the data. – Scuzzy Jul 29 '18 at 21:55
  • I used `print_r()`: `Array ( [0] => /test/ [1] => /test/ [2] => [4] => [6] => [8] => ) Array ( [0] => replace_test [1] => replace_test [2] => [4] => [6] => [8] => )` – Jesty Jul 29 '18 at 21:59
  • And with `var_dump`: `array(10) { [0]=> string(6) "/test/" [1]=> string(6) "/test/" [2]=> string(8) " string(10) "" [4]=> string(8) " string(10) "" [6]=> string(8) " string(10) "" [8]=> string(8) " string(10) "" } array(10) { [0]=> string(12) "replace_test" [1]=> string(12) "replace_test" [2]=> string(4) " string(16) "" [4]=> string(4) " string(16) "" [6]=> string(4) " string(16) "" [8]=> string(4) " string(16) "" } ` – Jesty Jul 29 '18 at 22:00
  • Those empty ones are probably going to be a problem because they are not a valid regular expression pattern. Either way I'm not seeing anything with `>` in it as per that error. Also can you try `preg_replace($this->config->find_string, $this->config->replace_string, $html, -1, $_count);` because the 4th parameter does not align with str_replace's 4th parameter – Scuzzy Jul 29 '18 at 22:10
  • Maybe I cannot use `$this->config->find_string` in `preg_replace()` as a parametr? And php thinks that `>` in that statment needs ending delimiter? – Jesty Jul 29 '18 at 22:16
  • Tried `preg_replace($this->config->find_string, $this->config->replace_string, $html, -1, $_count);` - the same error... – Jesty Jul 29 '18 at 22:19
  • An array of regex is fine https://3v4l.org/hJtRl having empty space is not https://3v4l.org/NO5sd having bad delimiters is not https://3v4l.org/dlo6P – Scuzzy Jul 29 '18 at 22:25
  • And none of the examples shows the same error `Warning: preg_replace(): No ending matching delimiter '>' found in` – Jesty Jul 29 '18 at 22:29
  • Since preg_replace() is expecting an un-parsed regex string, you have to add delimiters and escape them as necessary. If you were not using arrays, you could construct the regex string dynamically `preg_replace('~' . $astring . '~', 'replace with this' )` But since you're passing an array, you have to include the delimiter when you construct/declare your array. `$rxary = [ '~' . $astring . '~', ...];` And, for safety, it's good practice to escape regex metachars in the variable `preg_quote($astring)` as well as escaping the delimiter your using, in this case, it's the `~`. –  Jul 29 '18 at 23:14
  • @sln as I said, even if I delete regex string at all, I recieve the same error. – Jesty Jul 29 '18 at 23:37

1 Answers1

0

Rewrite your process function like this:

public function process($html, $url, $smart_tidy=true) {

    // a lot of code goes before

    // do string replacements
    if (!empty($this->config->find_string)) {
        if (count($this->config->find_string) == count($this->config->replace_string)) {



    $new_config_find_string = array_map(function($new_pattern)
    {
        return '/'.preg_quote($new_pattern).'/';
    },$this->config->find_string);


    $html = preg_replace($new_config_find_string, $this->config->replace_string, $html, $_count);

            $this->debug("Strings replaced: $_count (find_string and/or replace_string)");
        } else {
            $this->debug('Skipped string replacement - incorrect number of find-replace strings in site config');
        }
        unset($_count);
    }

    // a lot of code goes after
}
Erisan Olasheni
  • 2,395
  • 17
  • 20
  • @Ersian it gives `Warning: preg_quote() expects parameter 1 to be string, array given in` that string – Jesty Jul 30 '18 at 00:04
  • after your change now it gives `Warning: preg_replace(): Unknown modifier 'a' in` the same string. And where exactly do I need to place first part of your code? – Jesty Jul 30 '18 at 00:11
  • please confirm if `$this->config->find_string` and `$this->config->replace_string` are both arrays, and it is better to show us the real values of it – Erisan Olasheni Jul 30 '18 at 00:22
  • arrays, you can see values in comments above. – Jesty Jul 30 '18 at 00:27
  • the same `Warning: preg_replace(): Unknown modifier 'a' in` the same string. – Jesty Jul 30 '18 at 00:35
  • `print_r($new_config_find_string);` gives: `Array ( [0] => /123/ [1] => /123/ [2] => /\ /\/ [4] => /\ /\/ [6] => /\ /\/ [8] => /\ /\/ ) ` Where `123` is find_string value in template file. – Jesty Jul 30 '18 at 00:45
  • that's according to what's in your $this->config->find_string array – Erisan Olasheni Jul 30 '18 at 07:02
  • I will still suggest that you give us exactly what is inside the `$this->config->find_string` and the `$this->config->replace_string` arrays – Erisan Olasheni Jul 30 '18 at 07:03
  • how to do that? – Jesty Jul 30 '18 at 08:01
  • you can user `var_dump($this->config->find_string);` `var_dump($this->config->replace_string);` And Paste in the results – Erisan Olasheni Jul 30 '18 at 08:28
  • `array(10) { [0]=> string(3) "123" [1]=> string(3) "123" [2]=> string(8) " string(10) "" [4]=> string(8) " string(10) "" [6]=> string(8) " string(10) "" [8]=> string(8) " string(10) "" }` `array(10) { [0]=> string(13) "replace_test1" [1]=> string(13) "replace_test1" [2]=> string(4) " string(16) "" [4]=> string(4) " string(16) "" [6]=> string(4) " string(16) "" [8]=> string(4) " string(16) "" } ` Where `123` is `find_string` value in template file. – Jesty Jul 30 '18 at 09:13
  • there are errors in the way these patterns are quoted, in your `$this->config->find_string` why are there empty strings? There should not be. You will have to work on that first, you cannot replace `nothing` with `something` – Erisan Olasheni Jul 30 '18 at 15:32
  • and why everything works well `str_replace()` and the same array? – Jesty Jul 30 '18 at 16:04