php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #37800 preg_replace limit parameter introduces odd behaviour
Submitted: 2006-06-14 11:03 UTC Modified: 2006-06-15 15:36 UTC
From: RQuadling at GMail dot com Assigned: andrei (profile)
Status: Closed Package: PCRE related
PHP Version: 5CVS-2006-06-14 (snap) OS: Windows XP SP2
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: RQuadling at GMail dot com
New email:
PHP Version: OS:

 

 [2006-06-14 11:03 UTC] RQuadling at GMail dot com
Description:
------------
The documentation for preg_replace's limit parameter is ...

"The maximum possible replacements for each pattern in each subject string. Defaults to -1 (no limit)."

So, this suggests that you can set this to limit the number of replacements when there is the possibility of more than 1 match.

It seems that this is not actually working as such.

The example code is completely contrived.

The code is attempting to insert a single WHERE clause into an existing SQL statement.

The WHERE clause has to appear before GROUP BY, HAVING, ORDER BY or OPTION, depending upon which one is present.

The limit parameter is behaving like a "must match x times before replacement occurs".

The return values for the incorrect behaviour is NULL.

The documentation for preg_replace says 

"If matches are found, the new subject will be returned, otherwise subject will be returned unchanged."

Which is also incorrect.

Reproduce code:
---------------
<?php
$s_query = 'SELECT Author, COUNT(*) AS Books FROM Authors GROUP BY Authors HAVING COUNT(*) > 10 ORDER BY Author';
$s_replace = 'WHERE Author LIKE \'A%\' \1';
$s_new_query_all = preg_replace
	(
	"`(GROUP BY|HAVING|ORDER BY|OPTION)`sim", 
	$s_replace,
	$s_query,
	-1,
	$i_count_all
	);

$s_new_query_one = preg_replace
	(
	"`(GROUP BY|HAVING|ORDER BY|OPTION)`sim", 
	$s_replace,
	$s_query,
	1,
	$i_count_one
	);

$s_new_query_two = preg_replace
	(
	"`(GROUP BY|HAVING|ORDER BY|OPTION)`sim", 
	$s_replace,
	$s_query,
	2,
	$i_count_two
	);

$s_new_query_three = preg_replace
	(
	"`(GROUP BY|HAVING|ORDER BY|OPTION)`sim", 
	$s_replace,
	$s_query,
	3,
	$i_count_three
	);

echo <<< END_REPORT
Original Query : $s_query
No limit       : ($i_count_all) $s_new_query_all
Limit to 1     : ($i_count_one) $s_new_query_one
Limit to 2     : ($i_count_two) $s_new_query_two
Limit to 3     : ($i_count_three) $s_new_query_three
END_REPORT;
?>

Expected result:
----------------
Original Query : SELECT Author, COUNT(*) AS Books FROM Authors GROUP BY Authors HAVING COUNT(*) > 10 ORDER BY Author
No limit       : (3) SELECT Author, COUNT(*) AS Books FROM Authors WHERE Author LIKE 'A%' GROUP BY Authors WHERE Author LIKE 'A%' HAVING COUNT(*) > 10 WHERE Author LIKE 'A%' ORDER BY Author
Limit to 1     : (1) SELECT Author, COUNT(*) AS Books FROM Authors WHERE Author LIKE 'A%' GROUP BY Authors HAVING COUNT(*) > 10 ORDER BY Author
Limit to 2     : (2) SELECT Author, COUNT(*) AS Books FROM Authors WHERE Author LIKE 'A%' GROUP BY Authors WHERE Author LIKE 'A%' HAVING COUNT(*) > 10 ORDER BY Author
Limit to 3     : (3) SELECT Author, COUNT(*) AS Books FROM Authors WHERE Author LIKE 'A%' GROUP BY Authors WHERE Author LIKE 'A%' HAVING COUNT(*) > 10 WHERE Author LIKE 'A%' ORDER BY Author

Actual result:
--------------
Original Query : SELECT Author, COUNT(*) AS Books FROM Authors GROUP BY Authors HAVING COUNT(*) > 10 ORDER BY Author
No limit       : (3) SELECT Author, COUNT(*) AS Books FROM Authors WHERE Author LIKE 'A%' GROUP BY Authors WHERE Author LIKE 'A%' HAVING COUNT(*) > 10 WHERE Author LIKE 'A%' ORDER BY Author
Limit to 1     : (1) 
Limit to 2     : (2) 
Limit to 3     : (3) SELECT Author, COUNT(*) AS Books FROM Authors WHERE Author LIKE 'A%' GROUP BY Authors WHERE Author LIKE 'A%' HAVING COUNT(*) > 10 WHERE Author LIKE 'A%' ORDER BY Author

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2006-06-14 16:42 UTC] sniper@php.net
Thank you for this bug report. To properly diagnose the problem, we
need a short but complete example script to be able to reproduce
this bug ourselves. 

A proper reproducing script starts with <?php and ends with ?>,
is max. 10-20 lines long and does not require any external 
resources such as databases, etc.

If possible, make the script source available online and provide
an URL to it here. Try to avoid embedding huge scripts into the report.


 [2006-06-15 08:21 UTC] RQuadling at GMail dot com
Sorry. Shorter script.

<?php
$s_string = '1111111111';
$s_search = '/1/';
$s_replace = 'One ';
$i_limit = 1;
$i_count = 0;


$s_output = preg_replace($s_search, $s_replace, $s_string, $i_limit, $i_count);
echo "Output = " . var_export($s_output, True) . "\n";
echo "Count  = $i_count\n";

$i_limit = strlen($s_string);
$s_output = preg_replace($s_search, $s_replace, $s_string, $i_limit, $i_count);
echo "Output = " . var_export($s_output, True) . "\n";
echo "Count  = $i_count\n";
?>

Expected output ...

Output = 'One 111111111'
Count  = 1
Output = 'One One One One One One One One One One '
Count  = 10

Actual output ...

Output = NULL
Count  = 1
Output = 'One One One One One One One One One One '
Count  = 10

2 errors.

1 - preg_replace is mishandling the limit parameter
2 - preg_replace is returning NULL when the limit parameter is NOT the number of times that the replace can take place.
 [2006-06-15 09:53 UTC] nlopess@php.net
While I can agree that your script doesn't output the expected result, lets Andrei decide what to do here. That parameter exists for a long time and I don't know if this problem is a regression or if its a feature :)
 [2006-06-15 09:59 UTC] RQuadling at GMail dot com
This "bug" or "feature" is also messing up the building of the extended CHM file for windows.

In phpdoc/htmlhelp/filter_files.php (// $Id: filter_files.php,v 1.17 2005/07/01 22:27:04 techtonik Exp $) Lines 113 to 118 - This code attempts to replace the first <hx> tag with a simpler title.

As a consequence, any HTML file which needs to be processed and contains more than 1 <hx> tag produces a NULL output.

I now have 5815 zero length files rather than proper HTML files.
 [2006-06-15 15:36 UTC] nlopess@php.net
This bug has been fixed in CVS.

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/.
 
Thank you for the report, and for helping us make PHP better.

after a quick investigation I found this was really a regression in php 5.2 and 6.
 
PHP Copyright © 2001-2019 The PHP Group
All rights reserved.
Last updated: Mon Nov 18 23:01:35 2019 UTC