Explore secure alternatives to create_function in PHP 8, including closures, anonymous classes, and named functions, to improve performance and security in your code.
You upgraded a site to PHP 8, hit refresh, and got a white screen. Somewhere in an old plugin sits a call to create_function(), and PHP 8 no longer knows what that is.
Here’s the part the old tutorials get wrong: create_function() wasn’t just discouraged in PHP 8. It was removed. It landed as deprecated in PHP 7.2, and PHP 8.0 deleted it outright. Call it now and you get a fatal error, not a warning. So this isn’t cleanup you do when you get around to it. If the code runs on PHP 8, it’s already broken.
The good news: everything create_function() did, the language now does better and safer. It built functions by passing PHP source as strings and running them through eval, which is exactly the injection risk it sounds like. The replacements below don’t touch eval at all.
Table of Contents
Closures
A closure is an anonymous function you assign to a variable or pass as an argument. It’s the direct swap for almost every create_function() call you’ll find. Where the old function forced you to smuggle outer variables in as strings, a closure just captures them with the use keyword, and your editor can actually see the code.
Example of a Closure:
<?php
// A simple closure that adds two numbers
$myFunc = function ( $arg1, $arg2 ) {
return $arg1 + $arg2;
};
echo $myFunc( 2, 3 ); // outputs 5If the body is a single expression, PHP 7.4 and later give you arrow functions, which capture the surrounding scope automatically with no use list:
<?php
$myFunc = fn ( $arg1, $arg2 ) => $arg1 + $arg2;
echo $myFunc( 2, 3 ); // outputs 5Closures cover callbacks, event listeners, and the short throwaway logic you pass to functions like array_map. For most legacy conversions, this is where you’ll land.
Anonymous Classes
Sometimes a single function isn’t enough and you want a small object with a method or two, but naming a whole class feels like overkill. That’s what anonymous classes are for. You define the logic inline and hand back an object, no class name required.
Example of an Anonymous Class:
<?php
// Define an anonymous class and assign it to a variable
$myClass = new class {
public function add( $arg1, $arg2 ) {
return $arg1 + $arg2;
}
};
echo $myClass->add( 2, 3 ); // outputs 5Reach for this when a one-off needs to hold state or satisfy an interface. If all you need is a callable, a closure is lighter.
Named Functions
Not everything should be anonymous. If the same logic gets called from more than one place, give it a name. A named function is easier to read, easier to test, and shows up in a stack trace when something breaks.
Example of a Named Function:
<?php
// Define a named function to add two numbers
function myFunc( $arg1, $arg2 ) {
return $arg1 + $arg2;
}
echo myFunc( 2, 3 ); // outputs 5Rule of thumb: if you’d call it twice, name it.
Replacement for create_function() in PHP 8
Now the honest caveat. Occasionally you inherit a codebase that builds functions from strings assembled at runtime, and rewriting every call to a closure isn’t realistic in one pass. For that narrow case you can define a shim that restores the old function’s signature so the site stops crashing.
Replacement for create_function() in PHP 8:
/**
* A replacement for the deprecated create_function function in PHP 8.
*
* @param string $arg The argument list for the anonymous function.
* @param string $body The body of the anonymous function.
* @return callable Returns a new anonymous function.
*/
if ( ! function_exists( 'create_function' ) ) {
function create_function( $arg, $body ) {
static $cache = []; // A cache for storing previously created functions.
static $max_cache_size = 64; // Maximum cache size.
static $sorter; // A function to sort the cache by usage hits.
if ( $sorter === null ) {
// Define the sorter function
$sorter = function ( $a, $b ) {
return $a['hits'] < $b['hits'] ? 1 : -1;
};
}
// Generate a unique key for the function
$crc = crc32( $arg . "\x00" . $body );
if ( isset( $cache[$crc] ) ) {
++$cache[$crc]['hits']; // Increment the hit counter for the cached function
return $cache[$crc]['function'];
}
// Clear the cache if the size limit is reached
if ( count( $cache ) >= $max_cache_size ) {
uasort( $cache, $sorter );
array_pop( $cache ); // Remove the least-used function from the cache
}
// Create and cache a new anonymous function using eval
$cache[$crc] = [
'function' => eval( "return function($arg) { $body };" ),
'hits' => 0,
];
return $cache[$crc]['function'];
}
}
Be clear about what this is. The shim still runs your strings through eval, so it carries the same injection risk that got create_function() removed in the first place. The cache saves you from re-compiling the same string twice, but it doesn’t make eval safe. Treat this as a bridge that buys you time to migrate, and never feed it anything a user can influence.
For real work, closures, arrow functions, anonymous classes, and named functions cover the ground, and they do it without eval.
Conclusion
create_function() was deprecated in PHP 7.2 and removed in PHP 8.0, so on a modern stack it’s a fatal error waiting to happen, not a style preference. Swap each call for a closure or arrow function, use an anonymous class when you need an object, and name anything you reuse.
If you truly can’t rewrite everything at once, the shim keeps the lights on. But it’s a stopgap, not a destination. The moment you have room, retire the eval and let the language do the work.



Nice! It works 🙂
Cool 😎