php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Doc Bug #15166 preg_replace issue when \\1 is followed by 1
Submitted: 2002-01-22 11:47 UTC Modified: 2003-01-20 13:52 UTC
Votes:3
Avg. Score:3.7 ± 0.5
Reproduced:3 of 3 (100.0%)
Same Version:1 (33.3%)
Same OS:2 (66.7%)
From: ppechoux at egrail dot com Assigned:
Status: Closed Package: Documentation problem
PHP Version: 4.1.1 OS: WINDOWS 2000
Private report: No CVE-ID: None
 [2002-01-22 11:47 UTC] ppechoux at egrail dot com
1/DETAIL:
When I am using preg_replace with \\1 to keep the first iteration retrieved, if \\1 is followed by a string starting by 1, this is removing the 1 of the string and losing the content of \\1.

Here is a script to show 4 test cases. The test2 and test4 failed because of this issue. 

The test3 is the solution i found to avoid this issue by inserting any character different from 1 just after \\1.

Thanks in advance for your help.
Pierre

<?
$value='<!DOCTYPE map SYSTEM "http://localhost/Gui/dtd/deployment/dbmap.dtd">';
$servname='10.0.9.91';
$servport='81';

// TITLE
print "<HR><CENTER>\n";
print("<b>BUG with <font color='red'>preg_replace</font> and <font color='red'>\\\\1</font> followed by a variable starting by 1</b><BR><BR>\n");
print("<B> WINDOWS 2000 platform, PHP version 4.1.1<BR></b>\n");
print("<xmp>ORIGINAL VALUE:".$value."</xmp>\n");
print "<HR></CENTER>\n";

// TEST 1 with servname='localhost';
$testservname='localhost';
$test=$value;
$expected='<!DOCTYPE map SYSTEM "http://'.$testservname.':'.$servport.'/Gui/dtd/deployment/dbmap.dtd">';
$test=preg_replace("/(<\!DOCTYPE\s+map\s+SYSTEM\s+\"http:\/\/)[^\/]+/i","\\1$testservname:$servport",$test);
$status ='OK';
if ($test != $expected) $status='WRONG';

print "<xmp>\n";
print_r("test1 SERVER NAME:$testservname	SERVER PORT:$servport\n");
print_r("test1 OPERATION: \$test=preg_replace(\"/(<\!DOCTYPE\s+map\s+SYSTEM\s+\"http:\/\/)[^\/]+/i\",\"\\\\1\$testservname:\$servport\",\$test);"."\n");
print_r("test1 RESULT  :".$test."\n");
print_r("test1 EXPECTED:".$expected."\n");
print "</xmp><BR>\n";
print("test1 STATUS  :<font color='red'>".$status."</font><BR><HR>\n");

//TEST 2 with servname = '10.0.9.91';
$test=$value;
$testservname=$servname;
$expected='<!DOCTYPE map SYSTEM "http://'.$testservname.':'.$servport.'/Gui/dtd/deployment/dbmap.dtd">';
$test=preg_replace("/(<\!DOCTYPE\s+map\s+SYSTEM\s+\"http:\/\/)[^\/]+/i","\\1$servname:$servport",$test);
$status ='OK';
if ($test != $expected) $status='WRONG';

print "<xmp>\n";
print_r("test2 SERVER NAME:$testservname	SERVER PORT:$servport\n");
print_r("test2 OPERATION: \$test=preg_replace(\"/(<\!DOCTYPE\s+map\s+SYSTEM\s+\"http:\/\/)[^\/]+/i\",\"\\\\1\$testservname:\$servport\",\$test);"."\n");
print_r("test2 RESULT  :".$test."\n");
print_r("test2 EXPECTED:".$expected."\n");
print"</xmp><BR>\n";
print("test2 STATUS  :<font color='red'>".$status."</font><BR><HR>\n");


// TEST3
$test=$value;
$testservname=$servname;
$expected='<!DOCTYPE map SYSTEM "http://'.$testservname.':'.$servport.'/Gui/dtd/deployment/dbmap.dtd">';
$test=preg_replace("/(<\!DOCTYPE\s+map\s+SYSTEM\s+\")http:\/\/[^\/]+/i","\\1http://$servname:$servport",$test);
$status ='OK';
if ($test != $expected) $status='WRONG';

print "<xmp>\n";
print_r("test3 SERVER NAME:$testservname	SERVER PORT:$servport\n");
print_r("test3 OPERATION: \$test=preg_replace(\"/(<\!DOCTYPE\s+map\s+SYSTEM\s+\")http:\/\/[^\/]+/i\",\"\\\\1http://\$testservname:\$servport\",\$test);"."\n");
print_r("test3 RESULT  :".$test."\n");
print_r("test3 EXPECTED:".$expected."\n");
print "</xmp><BR>\n";
print("test3 STATUS  :<font color='red'>".$status."</font><BR><HR>\n");


// TEST4
$test="this is a test with any string";
$testservname='1234567890';
$expected='this is a '.$testservname.':'.$servport.' with any string';
$test=preg_replace("/(this is a )test/i","\\1$testservname:$servport",$test);
$status ='OK';
if ($test != $expected) $status='WRONG';

print "<xmp>\n";
print_r("test4 SERVER NAME:$testservname	SERVER PORT:$servport\n");
print_r("test4 OPERATION: \$test=preg_replace(\"/(this is a)test/i\",\"\\\\1\$testservname:\$servport\",\$test);"."\n");
print_r("test4 RESULT  :".$test."\n");
print_r("test4 EXPECTED:".$expected."\n");
print "</xmp><BR>\n";
print("test4 STATUS  :<font color='red'>".$status."</font><BR><HR>\n");

// General comments
print "<BR><b>COMMENTS</b>: test2 and test4 failed<BR>\n";
print "The issue happens only if \$testservname start by 1. For example with test1 the substitution works since the value of \$testservname does not start by 1\n";
print "<HR>\n";
?>

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2002-01-22 14:44 UTC] sander@php.net
\\11 matches the 11th string enclosed by parenthesis.
Can't find anything about escaping it. Reclassified as a documentation problem.
 [2002-01-22 15:00 UTC] ppechoux at egrail dot com
What do you think about the following suggestions:

A/ Counting how many string are enclosed by parentheses.
preg_replace("/(......)test(....)/","\\110.0.0.173",$test);
=> expecting 2 strings enclosed
Then parse the value following \\ and keep only the one lower than the max expected.
Expecting 2:
\\1 OK
\\11 TOO high, keep \\1

I do not know what the impact might be in term of performance.
=> Implementing a way to escape might be faster.

B/ Using a different notation to escape the string following \\1 (brace for example)
preg_replace("/(......)test(....)/","\\{1}10.0.0.173",$test);

Thanks for your prompt answer, that was quick :-)
 [2002-01-22 17:26 UTC] irc-html@php.net
You'd either need to escape the number after your reference, ie: \\1\1 or if you're using PHP 4.0.4 or later, you can use {$1} in the replacement portion of preg_replace (see documentation).
 [2002-01-24 11:27 UTC] ppechoux at egrail dot com
I tried the syntax {$1} with PHP 4.1.1 as below:
1/
$test=preg_replace("/(<\!DOCTYPE\s+map\s+SYSTEM\s+\"http:\/\/)[^\/]+/i","{$1}$servname:$servport",$test);

It gives me the following error:
Parse error: parse error, expecting `T_VARIABLE' or `'$'' in C:\inetpub\wwwroot\bug2.php on line 33

2/ Escaping the first brace:
$test=preg_replace("/(<\!DOCTYPE\s+map\s+SYSTEM\s+\"http:\/\/)[^\/]+/i","\{$1}$servname:$servport",$test);

The result is the following one:
test2 RESULT  :{<!DOCTYPE map SYSTEM "http://}10.0.9.91:81/Gui/dtd/deployment/dbmap.dtd">
It is escaping properly the 1 contained in $servername but keep the braces.

3/ The only way I found to escape $servername is by using the value 99 as follow:
$test=preg_replace("/(<\!DOCTYPE\s+map\s+SYSTEM\s+\"http:\/\/)[^\/]+/i","\\1\\99$servname:$servport",$test);
991 is not taken because of the limitation to 99 different strings. I just need to make sure I am not using 99 different parentheses which is pretty huge.

 [2002-01-24 11:32 UTC] derick@php.net
'{$1}' or "{\$1}"
 [2003-01-20 13:52 UTC] pollita@php.net
This bug has been fixed in CVS.

In case this was a PHP problem, snapshots of the sources are packaged
every three hours; this change will be in the next snapshot. You can
grab the snapshot at http://snaps.php.net/.
 
In case this was a documentation problem, the fix will show up soon at
http://www.php.net/manual/.

In case this was a PHP.net website problem, the change will show
up on the PHP.net site and on the mirror sites in short time.
 
Thank you for the report, and for helping us make PHP better.


 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Mon Jun 17 17:01:29 2024 UTC