TubeSum ← Transcribe a video

New in PHP 8.6: Faster array_map with first-class callables

0h 13m video Published Jul 13, 2026 Transcribed Aug 3, 2026 T Tideways
Intermediate 5 min read For: PHP developers interested in performance optimization and upcoming language features.
AI Trust Score 70/100
⚠️ Average / Some Fluff

"Delivers on the promise of explaining the new array_map optimization, but includes a lengthy intro and some filler."

AI Summary

This video discusses a new optimization in PHP 8.6 that automatically rewrites array_map calls with first-class callables into equivalent foreach loops, improving performance. The presenter, Benjamin, explains the current performance gap between array_map and foreach, the proposed patch, its limitations, and future improvements like partial function application and scoped functions that could enable full inlining.

[00:02]
Introduction to array_map optimization

PHP 8.6 will include an optimization for array_map that automatically makes some code faster, continuing the effort to improve PHP performance in every release.

[00:44]
Comparing array_map and foreach

Two code snippets that do the same thing: one uses array_map with a callable, the other uses foreach. The presenter sets up a benchmark with 100,000 values to compare performance.

[01:57]
Benchmark results: foreach is faster

Using hyperfine, the array_map version is significantly slower than the foreach version because foreach avoids function call overhead.

[02:41]
Proposed patch: Zen compile optimize array_map

Tim proposed a patch for PHP 8.6 that rewrites array_map with a callable into a foreach loop, measuring a 1.1x speedup (10% improvement) and 1.7x with JIT.

[04:21]
Supported callables: first-class callables only

The optimization only supports first-class callables (using the ... operator or the older string syntax). Closures (short or original) are not supported, which is a common use case.

[05:35]
Partial function application

PHP 8.6 will also introduce partial function application, which allows creating closures with some arguments pre-filled. This is also supported for the array_map optimization.

[07:20]
Scoped functions RFC

A future RFC on scoped functions could allow inlining closures entirely, enabling array_map, array_filter, and usort to be optimized at the compiler level to eliminate per-call overhead.

[10:14]
Real-world example from Doctrine

The optimization idea came from a Doctrine hot path where array_map was used. Rewriting it to foreach resulted in a 56% speedup for 200,000 calls, as shown in Tideways profiler results.

[12:14]
Recommendation: rewrite array_map to foreach

Until the optimization is fully in place, it is recommended to manually rewrite array_map to foreach in tight loops for performance.

[12:44]
Conclusion: PHP gets faster automatically

PHP improves performance in every release with optimizations like this, similar to the PHP 8.4 sprintf optimization, so upgrading automatically makes code faster.

PHP 8.6 introduces an optimization that automatically converts array_map with first-class callables to foreach, improving performance. While the current patch offers modest gains, future features like partial function application and scoped functions could enable full inlining, making PHP faster without developer effort.

Mentioned in this Video

Study Flashcards (8)

What is the main optimization proposed for PHP 8.6 regarding array_map?

easy Click to reveal answer

Automatically rewriting array_map with a first-class callable into a foreach loop.

02:41

What is the measured speedup of the array_map optimization without JIT?

easy Click to reveal answer

1.1 times faster (10% improvement).

03:20

What is the measured speedup with JIT?

medium Click to reveal answer

1.7 times faster.

04:05

Which callables are supported by the array_map optimization?

medium Click to reveal answer

First-class callables (using ... operator or string syntax).

04:36

Which callables are NOT supported by the optimization?

easy Click to reveal answer

Closures (short or original syntax).

05:08

What is partial function application?

medium Click to reveal answer

A feature that returns a closure with some arguments pre-filled, allowing simplification of function chains.

05:50

What is the scoped functions RFC?

hard Click to reveal answer

A proposal to define closures that share scope with the parent, potentially enabling inlining of closures into the calling code.

07:20

What was the performance improvement in the Doctrine example when rewriting array_map to foreach?

medium Click to reveal answer

56% faster for 200,000 calls.

11:58

💡 Key Takeaways

🔧

Proposed patch for array_map optimization

This is the core innovation of the video, showing a concrete way to improve PHP performance automatically.

02:41
📊

Partial function application support

This new feature expands the cases where the optimization can be applied, making it more useful.

05:35
💡

Scoped functions RFC for inlining

This future feature could eliminate function call overhead entirely, representing a significant performance leap.

07:20
📊

Doctrine hot path 56% speedup

Real-world evidence that rewriting array_map to foreach can yield substantial performance gains in critical code paths.

11:58

[00:02] optimization for array_map. It will make some code faster automatically, which is another step in the continuous effort to make PHP faster in every release. I will explain this optimization in this video and give an outlook on even more

[00:16] improvements using function inlining, which is already being worked on. But before we look into this, we are close to 1,000 subscribers on this channels. And if you have not subscribed yet, please do so. I am Benjamin and

[00:31] I've built a PHP profiler for the last 10 years, helping thousands of performance. If you look at the following two snippets of code, you will see that they are doing exactly the same.

[00:44] are doing exactly the same. Both have a list of values as input. So, Both have a list of values as input. So, the identifier is a one, a string foo, and an enum months January. And then

[00:58] one is iterating over this identifier using array_map, calling a function using array_map, calling a function identify identity or enum value. Checking for backed enum, returning the value or this value.

[01:12] And the for each snippet below is doing the same, for eaching and then changing those values. So, which snippet is faster than the other one? Let's extend the example to actually run. So, we also add an additional an

[01:30] additional 100,000 values into the arrays here. And the reason for this is that we want to test the performance of the PHP code and not of the startup phase of PHP itself. So, let's create a new terminal. And

[01:45] then we switch into the directory. We run hyperfine on this two scripts that compares the performance. And then we can easily see

[01:57] that the version intro one which uses array map is significantly slower than the one using for each. slower than the one using for each. So obviously for each is much faster

[02:11] So obviously for each is much faster because it's not calling a function. because it's not calling a function. It iterates and executes a PHP up code in the loop here whereas array map approach calls a

[02:25] function and there's a significant overhead for every function call in the PHP language. So what if you could automatically rewrite the array map code into the for each code? And this is something that my colleague Tim looked

[02:41] For PHP 8.6 he proposed the following patch called Zen compile optimize array map with a callable convert callback into for each.

[02:53] So you can see what he suggests here if we have a what he suggests here if we have a function then we have function then we have an array 100 times and then we iterate

[03:05] over this 100,000 times and then we call array map and if that is automatically transformed into a for each instead then he measured this to be 1.1 times

[03:20] then he measured this to be 1.1 times faster. So why only 1.1 times faster faster. So why only 1.1 times faster when we saw a fivefold difference in the example before? The way his patch rewrites the code is

[03:32] still includes the function call. So as you can see here it is rewritten as an assignment to a temporary array iterating over the identifier passing the value to this function and setting it to the key here.

[03:47] So there is still the function overhead and the only optimization is not calling array map and instead using a for each. And you can already see um uh small improvement on that. He measured 1.1 uh percent uh uh 1.1 improvement, so 10%

[04:05] and for the use of um this function with jit, he even measured an improvement uh that is 1.7 times faster. So, this is the first step into making this optimization possible of turning array map completely

[04:21] into for each, but it has a few restrictions. Um let's look at what kind of callables are supported in this rewrite. Um again, uh going back to Visual Studio and this example, so we have identifier three

[04:36] different values, identity or enum value function, and then what is supported is first-class callables. So, what are first-class callables? So, it's either the function using the we dot operator here, which um turns

[04:53] this into this older syntax to represent a callback. So, the the these two approaches are supported. What it's not supported is using a closure inside array map, so uh

[05:08] both the short closure or the original closure syntax is not supported. It cannot be rewritten this way. cannot be rewritten this way. So, arguably, this is um a use case that

[05:20] is visible much or more used more presently. So, array map is usually used with a callable and it's not used with a function uh a first-class callable, I would say in 95% of the cases.

[05:35] But, PHP 8.6 will also get a new function uh a new syntax or new feature called uh partial functional application. Let's look at that. Because this is also supported for this optimization.

[05:50] Partial function application means that if you have certain functions and that already exist and you want to use them and simplify them in a chain then what you can do is

[06:05] for a function that you previously called with three numeric arguments, you can use this syntax here and this will will not call foo but it will instead will not call foo but it will instead return a closure where the only variable

[06:21] that is left is this a question mark parameter here. see if we can find an example here.

[06:34] these are all very complex examples, so let's hope we have a simpler one. So a simpler example is here in preparation for the pipe operator RFC which this can be connected with quite well.

[06:48] You can see array map and you use in array and specify the second and the third argument to in array but you have the first one still variable, so this

[07:03] returns a closure now where only the first argument is actually passed to the corresponding closure calling in array with legal and strict two being set to these values on every iteration. This is still not a full support for

[07:20] converting array map to for each, but there's something else on the horizon that is not yet voted for inclusion into PHP, but it could be. The RFC on scoped functions. What does this do? It's

[07:34] third way of defining a closure and it works in a way where um it potentially allows inlining the whole closure into another set of code. So,

[07:48] let's see how it looks like. So, if we want all variables of a closure to be also available in the outside scope, then we need to uh use and then use

[08:01] reference parameters this way. And the scope functions RFC would introduce a new syntax that would automatically do this for all the all the variables that are used in the scope function, essentially making it so that

[08:17] variables between the parent and the um child function are shared. So, they are within the same scope. Uh how would this look like? We use the function keyword and then instead of the arrow operator, which makes uh which

[08:33] defines the short closure syntax, the one-line closures, it will use the um one-line closures, it will use the um square brackets and then declare a body as usual. And that would return a closure that shares the scope between

[08:49] parent and children. And if we have this, this would allow using this kind of closure and uh defined in the RFC, you see in future scope inlining,

[09:01] that it's possible to inline these kind of functions into the parent scope and that would allow, for example, array map, array filter, use sort, and others to be optimized at the compiler level to eliminate per call overhead when

[09:17] invoked. So, let's look at the code how this would look like uh using scope functions. Uh you can see an error still here because the syntax is not valid yet, um but you would define array map, the

[09:31] short function keyword with a block, and then return mixed instance of backed enum mixed value, or mixed and pass the identifier. And then in the future, this could be rewritten to for each.

[09:45] And then, if supported, this closure could also be inlined into the body. And then, it's actually the writing this is the same as writing this iteration over the identifier and

[10:00] calling this code. I think this is a great example of how PHP can improve the performance without you having to change the code. Just upgrading to newer versions make different parts of the application

[10:14] slightly faster, leading to an overall compounding effect of faster PHP code every time. And the idea for this optimization came from a doctrine example where I had a case of an array map that is

[10:32] called in the hot pass of doctrine. And something that the doctrine team has quite at heart is that the hot pass of reading database then again writing objects back into the database

[10:48] is considered the hot pass that needs to be optimized be optimized extremely because this is code that will be called in loops and doing things

[11:01] that loops of rows of database rows and doing inefficient coding there will definitely make the doctrine ORM slower and this immediately impacts all users So, let's look at the code

[11:15] code pull request that had this in doctrine. So, you can see here refactor array map into simple loop for poor performance. We look at the pull request that I made. It was about two two years ago.

[11:30] It was about two two years ago. Um, just changed previously um this array map uh which is doing what we've seen over the over this video over and over again, deciding to either return the backed

[11:43] enum value or the original value. And using array map to do that and this And using array map to do that and this was changed into an for each loop. And as you can see here in the Tideways profiler results for 200,000 calls,

[11:58] um it is 56% faster to use for each versus using array map. So, having this code in the hot path of your application, it makes sense to rewrite this from array map to for each. And unless we have this

[12:14] optimization in place in PHP core, which might be in PHP 8.6 or 8.7 or even further into the future, I recommend if you see this array map being called a lot of times in your code inside tight loops, then you should

[12:29] rewrite and use for each instead. To conclude this video, PHP gets faster in every release with optimizations like these. Previously slower code can be transformed by the engine to run faster in ways that you don't need to care

[12:44] about. You don't need to have this concern in mind. It just gets uh slightly faster on every version. This is similar for example to the PHP This is similar for example to the PHP 8.4 optimization of sprintf which can

[12:57] also be rewritten to um string interpolation um if you call it at the engine level. If you like this video and content about PHP performance, please consider subscribing to this channel or to the

[13:12] Tideways newsletter. Link is in the description. Bye.

More from Tideways

View all

⚡ Saved you 0h 13m reading this? Transcribe any YouTube video for free — no signup needed.