[PHP-DEV] array_reduce callback key

Hi Internals,

I just ran into a case where I needed the array key in an array_reduce() callback, but I can't access it :frowning:

So I wrote a PR to fix it: Added "key" parameter to `array_reduce` callback by Bilge · Pull Request #14986 · php/php-src · GitHub

What do you think? Does this need an RFC?

Cheers,
Bilge

On Wed, 17 Jul 2024, 01:29 mickmackusa, <mickmackusa@gmail.com> wrote:

It is untrue that you “can’t access it”; you just need to use an array of keys as the input array.

It is absolutely true and your workaround is just that; a workaround that doesn’t work at all when you also need the value.

Cheers,
Bilge

This is not the first time that a developer needs access to the input array’s keys within the callback of array_reduce().
It is untrue that you “can’t access it”; you just need to use an array of keys as the input array.

Some frameworks already have the feature of key access built into their methods/functions.
https://laravel.com/docs/11.x/collections#method-reduce

Examples of passing array_keys to array_reduce (some of which are more than a decade old):
https://stackoverflow.com/a/10196070/2943403

https://stackoverflow.com/a/27419768/2943403
https://stackoverflow.com/a/27809675/2943403

https://stackoverflow.com/a/32226816/2943403
https://stackoverflow.com/a/33372066/2943403
https://stackoverflow.com/a/37728665/2943403
https://stackoverflow.com/a/39322532/2943403

https://stackoverflow.com/a/45508817/2943403

https://stackoverflow.com/a/48664250/2943403

https://stackoverflow.com/a/49035053/2943403

https://stackoverflow.com/a/57966850/2943403
https://stackoverflow.com/a/58997848/2943403

https://stackoverflow.com/a/73726419/2943403

https://stackoverflow.com/a/76196482/2943403

mickmackusa

On Wed, 17 Jul 2024, 09:15 Bilge, <bilge@scriptfusion.com> wrote:

Hi Internals,

I just ran into a case where I needed the array key in an array_reduce()
callback, but I can’t access it :frowning:

So I wrote a PR to fix it: https://github.com/php/php-src/pull/14986

What do you think? Does this need an RFC?

Cheers,
Bilge

On Tue, Jul 16, 2024 at 5:17 PM Bilge <bilge@scriptfusion.com> wrote:

Hi Internals,

I just ran into a case where I needed the array key in an array_reduce()
callback, but I can't access it :frowning:

So I wrote a PR to fix it: Added "key" parameter to `array_reduce` callback by Bilge · Pull Request #14986 · php/php-src · GitHub

What do you think? Does this need an RFC?

Cheers,
Bilge

I don't think it's reasonable to cover every use-case "nicely" in the
standard library. You didn't give a real example. You might consider
using array_walk or iterator_apply, or a foreach loop depending on
what you need.

I am curious what your real use-case is. It may be worth adding
something. It might not. Hard to tell. Adding arguments to a function
can mess up internal callbacks, btw, so I don't like modifying the
existing function.

On 17/07/2024 01:41, Levi Morrison wrote:

Adding arguments to a function can mess up internal callbacks, btw, so I don't like modifying the
existing function.

Which internal callbacks can be messed up by this change? They clearly aren't tested, as the build is passing.

Cheers,
Bilge

On Wed, Jul 17, 2024, at 09:55, Lily Bergonzat wrote:

While it is indeed a workaround, you can very easily

access the values of the array from the keys, since that

literally is what the keys are for.

While I also think it’s weird that not all php array functions

with callbacks do pass the key as an argument, and while

I also stumbled upon that problem in the past myself

multiple times, I also think it is worth taking the time to

consider what other people from this mailing list are

bringing up. If it could indeed break internal callbacks,

then I think using the workaround could be a better idea

than potentially breaking other things.

On Wed, Jul 17, 2024 at 2:38 AM Bilge <bilge@scriptfusion.com> wrote:

On Wed, 17 Jul 2024, 01:29 mickmackusa, <mickmackusa@gmail.com> wrote:

It is untrue that you “can’t access it”; you just need to use an array of keys as the input array.

It is absolutely true and your workaround is just that; a workaround that doesn’t work at all when you also need the value.

Cheers,

Bilge

If it can be the last argument, then PHP doesn’t care if you call a function with extra arguments … at least, it usually doesn’t.

— Rob

While it *is* indeed a workaround, you can very easily
access the values of the array from the keys, since that
literally is what the keys are for.

While I also think it's weird that not all php array functions
with callbacks do pass the key as an argument, and while
I also stumbled upon that problem in the past myself
multiple times, I also think it is worth taking the time to
consider what other people from this mailing list are
bringing up. If it could indeed break internal callbacks,
then I think using the workaround could be a better idea
than potentially breaking other things.

On Wed, Jul 17, 2024 at 2:38 AM Bilge <bilge@scriptfusion.com> wrote:

On Wed, 17 Jul 2024, 01:29 mickmackusa, <mickmackusa@gmail.com> wrote:

It is untrue that you "can't access it"; you just need to use an array of keys as the input array.

It is absolutely true and your workaround is just that; a workaround that doesn't work at all when you also need the value.

Cheers,
Bilge

It would make sense for it to be the last argument. My first thought was also
that PHP doesn't usually care if you add extra arguments, but "internal
callbacks" made me think they were C callbacks, and in that case I don't
really know.

On Wed, Jul 17, 2024 at 10:08 AM Rob Landers <rob@bottled.codes> wrote:

On Wed, Jul 17, 2024, at 09:55, Lily Bergonzat wrote:

While it *is* indeed a workaround, you can very easily
access the values of the array from the keys, since that
literally is what the keys are for.

While I also think it's weird that not all php array functions
with callbacks do pass the key as an argument, and while
I also stumbled upon that problem in the past myself
multiple times, I also think it is worth taking the time to
consider what other people from this mailing list are
bringing up. If it could indeed break internal callbacks,
then I think using the workaround could be a better idea
than potentially breaking other things.

On Wed, Jul 17, 2024 at 2:38 AM Bilge <bilge@scriptfusion.com> wrote:
>
> On Wed, 17 Jul 2024, 01:29 mickmackusa, <mickmackusa@gmail.com> wrote:
>>
>> It is untrue that you "can't access it"; you just need to use an array of keys as the input array.
>
> It is absolutely true and your workaround is just that; a workaround that doesn't work at all when you also need the value.
>
> Cheers,
> Bilge

If it can be the last argument, then PHP doesn't care if you call a function with extra arguments ... at least, it usually doesn't.

— Rob

On Wed, Jul 17, 2024, at 9:01 AM, Bilge wrote:

On 17/07/2024 01:41, Levi Morrison wrote:

Adding arguments to a function can mess up internal callbacks, btw, so I don't like modifying the
existing function.

Which internal callbacks can be messed up by this change? They clearly
aren't tested, as the build is passing.

Cheers,
Bilge

There's a number of issues here.

PHP-defined functions ignore excess arguments.

C-defined functions error on excess arguments.

A function with an optional second argument... may break if it gets passed an optional argument that isn't aligned with its expectation. intval() is the usual example (PHP: intval - Manual), but it would also impact something like trim(). If someone is using a function with one param and one optional param as a callback right now, suddenly passing the key as the second argument could cause all sorts of weirdness.

Similarly, if someone is using a single-param C-defined function as a callback right now, and array_reduce() starts passing a second argument, it will fatal.

I ran into this issue while building Crell/fp, and my only solution (after swearing a lot) was to just make two separate versions of every operation: One that passes the key and one that doesn't. (cf: fp/src/array.php at master · Crell/fp · GitHub) The user has to pick the right one. That is, sadly, the only reliable solution, even if done in the stdlib.

(Of course, the real solution is to introduce a separate map/dict type that has its own dedicated API for map/filter/reduce, but now we're well out of scope... Using the same structure for lists and maps is PHP's original sin.)

--Larry Garfield