| 
        php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login | 
  [2012-04-20 21:40 UTC] 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
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits             
             | 
    |||||||||||||||||||||||||||
            
                 
                Copyright © 2001-2025 The PHP GroupAll rights reserved.  | 
        Last updated: Tue Nov 04 05:00:01 2025 UTC | 
@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 ); }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'] ------It'd a bug, because: <? //PHP Version 5.4.4-10, memory=128MB $source = "123"; //$source = preg_replace_callback('/\d+/', function($m){return $m[0];}, $source); //error Allowed memory size //$source = preg_replace_callback('/\d+/', function() use($m){return $m[0];}, $source); //error Allowed memory size //$source = preg_replace_callback('/\d+/', create_function('$m', 'return $m[0];'), $source); //work, but create_function for it's version php is old, about it in executed not notice, but notice in documentation //$source = preg_replace_callback('/\d+/', function(){return;}, $source); //error Allowed memory size //$source = preg_replace_callback('/\d+/', create_function('',''), $source); //work, but construction is old //$f=function(){return;}; $source = preg_replace_callback('/\d+/', $f, $source); //error Allowed memory size //function f(){return;}; $source = preg_replace_callback('/\d+/', 'f', $source); //work, but it's method unacceptable if need do loop and taken out of the loop is also impossible, because key is change and will protection of engine if(!function_exists('f')){function f(){return;};} $source = preg_replace_callback('/\d+/', 'f', $source); //work, not be deprecated and not suitable for loop ?>