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