Edit report at https://bugs.php.net/bug.php?id=61792&edit=1

 ID:                 61792
 Comment by:         andrew at mcnaughty dot com
 Reported by:        tshaw at oitc dot com
 Summary:            preg_replace_callback memory leak
 Status:             Not a bug
 Type:               Bug
 Package:            PCRE related
 Operating System:   OSX 10.7.3
 PHP Version:        5.4.0
 Block user comment: N
 Private report:     N

 New Comment:

Actually I think this bug or something very like it still exists with an 
anonymous function:

I'm seeing a leak with the following code:

------
        $this->contact['email_greeting_display'] = preg_replace_callback(
            '@\{(?:contact\.)?([a-z0-9._]*)\}@',
            function($matches) use ($prefixes,$contact) {
                if ($matches[1] == 'individual_prefix') {
                    return $prefixes[$contact['prefix_id']];
                }
                else {
                    return $contact[$matches[1]];
                }
            },
            $format['greeting']
------


Previous Comments:
------------------------------------------------------------------------
[2012-04-21 01:46:59] anon at anon dot anon

@tshaw: The problem is that create_function is a nasty old construct that adds 
a new function every time you call it, even if the code to compile is the same 
each time. The created functions are *permanent* and create_function returns 
their name only -- so even if the variable containing their name goes out of 
scope, the created functions persist. Naturally, 10 million functions take a 
lot of memory. It's not technically a bug, just awful language design.

You can create the functions once statically and store their names, or since 
PHP 5.3.0, you can use anonymous functions instead. Try this:

function urlDecodeUnreservedChars( $string ) {
        $unreserved = array();
        $unreserved[] = dechex( ord( '-' ) );
        $unreserved[] = dechex( ord( '.' ) );
        $unreserved[] = dechex( ord( '_' ) );
        $unreserved[] = dechex( ord( '~' ) );
        return preg_replace_callback(
                array_map(function ($str) { return '/%' . strtoupper($str) . 
'/x'; }, $unreserved),
                function ($matches) { return chr(hexdec($matches[0])); }, 
$string
        );
}

------------------------------------------------------------------------
[2012-04-20 22:45:09] tshaw at oitc dot com

I am totally confused. 

You say that its OK for PHP to fail with a memory exhausted error when running 
a 
perfectly valid CLI script that happens to fail on the 180951 iteration? I say 
this is absolutely bug!  

There may be no memory leak but it surely is a bug as a CLI script that 
iterates 
a long period of time is not out of line.

I respectfully request you reconsider and change this back to a bug.

------------------------------------------------------------------------
[2012-04-20 22:24:28] fel...@php.net

Thank you for taking the time to write to us, but this is not
a bug. Please double-check the documentation available at
http://www.php.net/manual/ and the instructions on how to report
a bug at http://bugs.php.net/how-to-report.php

There is no memory leak, what happens is that the memory associated to the 
lambda functions (via create_function()) is just released in the end of 
execution.

------------------------------------------------------------------------
[2012-04-20 21:40:54] tshaw at oitc dot com

Description:
------------
$ ./ptest.php
Test preg_replace_callback
Iteration number 0
Iteration number 1
....
Iteration number 180951
PHP Fatal error:  Allowed memory size of 268435456 bytes exhausted (tried to 
allocate 3072 bytes) in /Users/tshaw/Sites/surbl/ptest.php(11) : 
runtime-created 
function on line 1

Fatal error: Allowed memory size of 268435456 bytes exhausted (tried to 
allocate 
3072 bytes) in /Users/tshaw/Sites/surbl/ptest.php(11) : runtime-created 
function 
on line 1


Test script:
---------------
#!/usr/local/php5/bin/php
<?PHP
// #!/usr/bin/php
error_reporting(E_ALL);
function urlDecodeUnreservedChars( $string ) {
        $unreserved = array();
        $unreserved[] = dechex( ord( '-' ) );
        $unreserved[] = dechex( ord( '.' ) );
        $unreserved[] = dechex( ord( '_' ) );
        $unreserved[] = dechex( ord( '~' ) );
        return preg_replace_callback( array_map( create_function( '$str', 
'return "/%" . strtoupper( $str ) . "/x";' ), $unreserved ), create_function( 
'$matches', 'return chr( hexdec( $matches[0] ));' ), $string );
    }
for ($i=0; $i <5000000; $i++) {
                echo "Iteration number $i\n";
        urlDecodeUnreservedChars( "12345" );
}
?>


Expected result:
----------------
Expected it to run to completion

Actual result:
--------------
PHP Fatal error:  Allowed memory size of 268435456 bytes exhausted (tried to 
allocate 3072 bytes) in /Users/tshaw/Sites/surbl/ptest.php(11) : 
runtime-created 
function on line 1

Fatal error: Allowed memory size of 268435456 bytes exhausted (tried to 
allocate 
3072 bytes) in /Users/tshaw/Sites/surbl/ptest.php(11) : runtime-created 
function 
on line 1



------------------------------------------------------------------------



-- 
Edit this bug report at https://bugs.php.net/bug.php?id=61792&edit=1

Reply via email to