[RFC] Add clamp function

php.internals

Kyle Katarn

1 year ago
Hello, I took a look at thinkverse <https://github.com/thinkverse> work on https://wiki.php.net/rfc/clamp and on the open issues (handling NAN and other comparable values not being integer nor float). This new implementation should resolve those issues: https://github.com/php/php-src/pull/19434 If you allow me, I would update the RFC to add this new implementation listed on it, remove the "Open issues" section if you don't see any with the new implementation and would change the status of this RFC to draft to reflect the fact that there is again an open PR for it. Thanks,

Tim Düsterhus

1 year ago
Hi Am 2025-08-19 09:32, schrieb Kyle Katarn:
> If you allow me, I would update the RFC to add this new implementation > listed on it, remove the "Open issues" section if you don't see any > with > the new implementation and would change the status of this RFC to draft > to > reflect the fact that there is again an open PR for it.
Please see https://wiki.php.net/rfc/howto#external_resources#rfcs_belong_to_a_single_author (the "RFCs 'belong' to a single author") section. The existing RFC may only be adjusted with the original author’s consent. It might be easiest for you to create a “clamp function v2” RFC instead. Best regards Tim Düsterhus

Kyle Katarn

1 year ago
Hi 2025-08-19 14:59, Tim Düsterhus <tim@bastelstu.be> wrote :
> Hi > > Am 2025-08-19 09:32, schrieb Kyle Katarn: > > If you allow me, I would update the RFC to add this new implementation > > listed on it, remove the "Open issues" section if you don't see any > > with > > the new implementation and would change the status of this RFC to draft > > to > > reflect the fact that there is again an open PR for it. > > Please see > > https://wiki.php.net/rfc/howto#external_resources#rfcs_belong_to_a_single_author > (the "RFCs 'belong' to a single author") section. The existing RFC may > only be adjusted with the original author’s consent. It might be easiest > for you to create a “clamp function v2” RFC instead. > > Best regards > Tim Düsterhus >
Thanks, when I go to https://wiki.php.net/RFC/clamp-v2 and click "Edit the page", it says: "Sorry, you don't have enough rights to continue." Can you grant me this permission? Thanks.

Christoph Becker

1 year ago
On 20.08.2025 at 09:29, Kyle Katarn wrote:
> 2025-08-19 14:59, Tim Düsterhus <tim@bastelstu.be> wrote :>>>
https://wiki.php.net/rfc/howto#external_resources#rfcs_belong_to_a_single_author
>> (the "RFCs 'belong' to a single author") section. The existing RFC may >> only be adjusted with the original author’s consent. It might be easiest >> for you to create a “clamp function v2” RFC instead. > > Thanks, when I go to https://wiki.php.net/RFC/clamp-v2 and click "Edit the > page", it says: > "Sorry, you don't have enough rights to continue." > > Can you grant me this permission? Thanks.
I've granted RFC karma to the kylekatarnls account; there is also a kylek account, though? Can that be removed? Christoph

Kyle Katarn

1 year ago
2025-08-20 at 14:28, Christoph M. Becker <cmbecker69@gmx.de> wrote :
> On 20.08.2025 at 09:29, Kyle Katarn wrote: > > > 2025-08-19 14:59, Tim Düsterhus <tim@bastelstu.be> wrote :>>> > > https://wiki.php.net/rfc/howto#external_resources#rfcs_belong_to_a_single_author > >> (the "RFCs 'belong' to a single author") section. The existing RFC may > >> only be adjusted with the original author’s consent. It might be easiest > >> for you to create a “clamp function v2” RFC instead. > > > > Thanks, when I go to https://wiki.php.net/RFC/clamp-v2 and click "Edit > the > > page", it says: > > "Sorry, you don't have enough rights to continue." > > > > Can you grant me this permission? Thanks. > > I've granted RFC karma to the kylekatarnls account; there is also a > kylek account, though? Can that be removed? > > Christoph >
Thanks, yes "kylek" account can be removed. I created https://wiki.php.net/rfc/clamp_v2 (re-using most of the info from https://wiki.php.net/rfc/clamp). Thanks for you

Juris Evertovskis

1 year ago
From: Kyle Katarn <kylekatarnls@gmail.com> Sent: Friday, August 22, 2025 11:02 AM To: Christoph M. Becker <cmbecker69@gmx.de> Cc: Tim Düsterhus <tim@bastelstu.be>; PHP Internals <internals@lists.php.net> Subject: Re: [PHP-DEV] [RFC] Add clamp function
> I created https://wiki.php.net/rfc/clamp_v2 (re-using most of the info from https://wiki.php.net/rfc/clamp).
Hey, I’m not against adding such a function, but I noticed that the RFC says
> some of which use min <https://www.php.net/manual/en/function.min.php> and max <https://www.php.net/manual/en/function.max.php> to check the bound, which is costly and slow when called often
What makes them slow? If we can create a significantly faster `clamp`, can we use the same optimizations to improve min and max? BR, Juris

Kyle Katarn

1 year ago
2025-08-22 at 13:30, Juris Evertovskis <juris@glaive.pro> wrote:
> *From:* Kyle Katarn <kylekatarnls@gmail.com> > *Sent:* Friday, August 22, 2025 11:02 AM > *To:* Christoph M. Becker <cmbecker69@gmx.de> > *Cc:* Tim Düsterhus <tim@bastelstu.be>; PHP Internals < > internals@lists.php.net> > *Subject:* Re: [PHP-DEV] [RFC] Add clamp function > > > I created https://wiki.php.net/rfc/clamp_v2 (re-using most of the info > from https://wiki.php.net/rfc/clamp). > > Hey, > > > > I’m not against adding such a function, but I noticed that the RFC says > > > > > some of which use min <https://www.php.net/manual/en/function.min.php> > and max <https://www.php.net/manual/en/function.max.php> to check the > bound, which is costly and slow when called often > > > > What makes them slow? If we can create a significantly faster `clamp`, can > we use the same optimizations to improve min and max? > > > > BR, > > Juris >
I taked it from https://wiki.php.net/rfc/clamp and from what I understand reading the code: min and max support variadic array of parameters, or a single array value, so min($max, max($min, $value)) will result in 2 loops, plus if-else paths depending on the type. While the current implementation of clamp accepts only 1 value, 1 min and 1 max, so it just does $value < $min ? $min : $value > $max ? $max : $value, which is way less work. Also from my point of view, min($max, max($min, $value)) has a counter-intuitive reading when used to clamp a value.

Weedpacket

1 year ago
On 2025-08-23 05:29, Kyle Katarn wrote:
> > Also from my point of view,  min($max, max($min, $value)) has a counter- > intuitive reading when used to clamp a value.
Well, that's trivial to deal with: min(max($min, $value), $max));

Kyle Katarn

1 year ago
2025-08-22 at 22:29, Morgan <Weedpacket@varteg.nz> wrote:
> On 2025-08-23 05:29, Kyle Katarn wrote: > > > > Also from my point of view, min($max, max($min, $value)) has a counter- > > intuitive reading when used to clamp a value. > > Well, that's trivial to deal with: > > min(max($min, $value), $max)); >
It's not just about the order of parameters, it's still not intuitive to call max() with $min and min() with $max, you still have to think about what it does step by step to make sense out of it, while clamp($value, min: $min, $max) or clamp(min: $min, value: $value, max: $max) will provide a syntax that immediately make sense for next reader. Like the author of the RFC v1 also mentioned, min(max()) is inefficient (see the inner complexity of those functions).

Weedpacket

1 year ago
On 2025-08-25 02:27, Kyle Katarn wrote:
> 2025-08-22 at 22:29, Morgan <Weedpacket@varteg.nz > <mailto:Weedpacket@varteg.nz>> wrote: > > On 2025-08-23 05:29, Kyle Katarn wrote: > > > > Also from my point of view,  min($max, max($min, $value)) has a > counter- > > intuitive reading when used to clamp a value. > > Well, that's trivial to deal with: > > min(max($min, $value), $max)); > > > It's not just about the order of parameters, it's still not intuitive to > call max() with $min and min() with $max, you still have to think about > what it does step by step to make sense out of it, while clamp($value, > min: $min, $max) or clamp(min: $min, value: $value, max: $max) will > provide a syntax that immediately make sense for next reader. >
Well, clamp(min,value,max) isn't that far from minmax(min,value,max) and then chuck a couple more parentheses in there. Still,
> Like the author of the RFC v1 also mentioned, min(max()) is inefficient > (see the inner complexity of those functions). >
Yes, min/max are regarded as array functions, and doesn't special-case the situation where two arguments are passed; no particular reason why that couldn't be done as it seems to be a common use case. Otherwise, the bulk of the complexity comes from type handling and that won't go away in a robust implementation of clamp. Note that I am not against introducing clamp(). It's certainly not going to hurt to write clamp(min:$min, value:$value, max:$max) instead of min(max($min, $value), $max). I'm going to have to remember to put the parameter names in, because "$min <= $value <= $max" seems by far the most natural ordering (and is how "this value lies between these two extremes" is written in maths). There is also the use case of returning something other than $min if $value<$min (and other than $max if $value>$max), allowing clamp to filter values that are within the range or too high or too low. That would mean another one or two optional parameters. For numeric values there's also the closely related rescale() function, which is basically rescale($value, $min, $max) = ($value - $min) / ($max - $min) (it maps the interval [0,1] to [$min,$max] and finds where $value gets carried to by the mapping). Again, a couple more parameters would allow specifying initial intervals other than [0,1].

Larry Garfield

1 year ago
On Fri, Aug 22, 2025, at 3:01 AM, Kyle Katarn wrote:
> 2025-08-20 at 14:28, Christoph M. Becker <cmbecker69@gmx.de> wrote : >> On 20.08.2025 at 09:29, Kyle Katarn wrote: >> >> > 2025-08-19 14:59, Tim Düsterhus <tim@bastelstu.be> wrote :>>> >> https://wiki.php.net/rfc/howto#external_resources#rfcs_belong_to_a_single_author <https://wiki.php.net/rfc/howto#external_resources%23rfcs_belong_to_a_single_author> >> >> (the "RFCs 'belong' to a single author") section. The existing RFC may >> >> only be adjusted with the original author’s consent. It might be easiest >> >> for you to create a “clamp function v2” RFC instead. >> > >> > Thanks, when I go to https://wiki.php.net/RFC/clamp-v2 and click "Edit the >> > page", it says: >> > "Sorry, you don't have enough rights to continue." >> > >> > Can you grant me this permission? Thanks. >> >> I've granted RFC karma to the kylekatarnls account; there is also a >> kylek account, though? Can that be removed? >> >> Christoph > > Thanks, yes "kylek" account can be removed. > > I created https://wiki.php.net/rfc/clamp_v2 (re-using most of the info > from https://wiki.php.net/rfc/clamp). > > Thanks for you
The grammar in this one sentence is very clumsy:
> clamp takes three arguments, a $num, $min and $max, checks if $num is within the bounds of $min and $max, if in range, returns the value of $num, otherwise, returns the nearest bound value, i.e. if $num > $max return $max, if $num < $min return $min.
Please break it up into multiple sentences so it's easier to follow. I think I follow it, but it's clunky enough that I am not certain of it. :-) Also, the text says $num but the code example says $value. What determines comparability? What happens if you try to clamp values that are not comparable? Eg: clamp(new Point(1, 2), new Point(0, 0), new Point(5, 5)); I assume that will fail somehow, but the failure should be described explicitly. --Larry Garfield

Kyle Katarn

1 year ago
2025-08-22 at 18:09, Larry Garfield <larry@garfieldtech.com> wrote:
> On Fri, Aug 22, 2025, at 3:01 AM, Kyle Katarn wrote: > > 2025-08-20 at 14:28, Christoph M. Becker <cmbecker69@gmx.de> wrote : > >> On 20.08.2025 at 09:29, Kyle Katarn wrote: > >> > >> > 2025-08-19 14:59, Tim Düsterhus <tim@bastelstu.be> wrote :>>> > >> > https://wiki.php.net/rfc/howto#external_resources#rfcs_belong_to_a_single_author > < > https://wiki.php.net/rfc/howto#external_resources%23rfcs_belong_to_a_single_author > > > >> >> (the "RFCs 'belong' to a single author") section. The existing RFC > may > >> >> only be adjusted with the original author’s consent. It might be > easiest > >> >> for you to create a “clamp function v2” RFC instead. > >> > > >> > Thanks, when I go to https://wiki.php.net/RFC/clamp-v2 and click > "Edit the > >> > page", it says: > >> > "Sorry, you don't have enough rights to continue." > >> > > >> > Can you grant me this permission? Thanks. > >> > >> I've granted RFC karma to the kylekatarnls account; there is also a > >> kylek account, though? Can that be removed? > >> > >> Christoph > > > > Thanks, yes "kylek" account can be removed. > > > > I created https://wiki.php.net/rfc/clamp_v2 (re-using most of the info > > from https://wiki.php.net/rfc/clamp). > > > > Thanks for you > > The grammar in this one sentence is very clumsy: > > > clamp takes three arguments, a $num, $min and $max, checks if $num is > within the bounds of $min and $max, if in range, returns the value of $num, > otherwise, returns the nearest bound value, i.e. if $num > $max return > $max, if $num < $min return $min. > > Please break it up into multiple sentences so it's easier to follow. I > think I follow it, but it's clunky enough that I am not certain of it. :-) > > Also, the text says $num but the code example says $value. > > What determines comparability? What happens if you try to clamp values > that are not comparable? Eg: > > clamp(new Point(1, 2), new Point(0, 0), new Point(5, 5)); > > I assume that will fail somehow, but the failure should be described > explicitly. > > --Larry Garfield >
Ah true, $num was v1 when it accepted only int|float, I switched to $value since it's now mixed. About "What determines comparability", it follows the usual rules of PHP: https://www.php.net/manual/en/language.operators.comparison.php So it's equivalent to ($value < $min) ? $min : (($value > $max) ? $max : $value) and also equivalent to min($max, max($min, $value)) About clamp(new Point(1, 2), new Point(0, 0), new Point(5, 5)); If Point is a comparable value (simple DTO for example), it should return $value, like when doing ($value < $min) ? $min : (($value > $max) ? $max : $value) we could add a test for such case, but I think that for consistency, whatever currently works in min() should work in clamp() Following the link of the implementation, there is also a link to the documentation where I already explained the comparison rules following the example of what was done in the documentation for min() and max(): https://github.com/php/doc-en/pull/4814

Larry Garfield

1 year ago
On Fri, Aug 22, 2025, at 12:45 PM, Kyle Katarn wrote:
> About "What determines comparability", it follows the usual rules of > PHP: https://www.php.net/manual/en/language.operators.comparison.php > > So it's equivalent to ($value < $min) ? $min : (($value > $max) ? $max > : $value) and also equivalent to min($max, max($min, $value)) > > About clamp(new Point(1, 2), new Point(0, 0), new Point(5, 5)); > > If Point is a comparable value (simple DTO for example), it should > return $value, like when doing ($value < $min) ? $min : (($value > > $max) ? $max : $value) we could add a test for such case, but I think > that for consistency, whatever currently works in min() should work in > clamp() > > Following the link of the implementation, there is also a link to the > documentation where I already explained the comparison rules following > the example of what was done in the documentation for min() and max(): > https://github.com/php/doc-en/pull/4814
Please make sure the above is captured in the RFC. Though apparently what PHP currently does with Point comparisons in min/max is... weird. I don't even know what the logic here is. :) https://3v4l.org/pTmiV (No need to change it in this RFC, just note explicitly that the expected behavior is identical to that min(max()) construct, regardless of type.) --Larry Garfield

Christoph Becker

1 year ago
On 22.08.2025 at 22:09, Larry Garfield wrote:
> Though apparently what PHP currently does with Point comparisons in min/max is... weird. I don't even know what the logic here is. :) > > https://3v4l.org/pTmiV
That's the usual PHP object/array comparison [1]: 10 > 2, and 5 < 10. The $y member isn't even checked. See <https://www.php.net/manual/en/language.operators.comparison.php#example-167>. Christoph

Rob Landers

1 year ago
On Fri, Aug 22, 2025, at 22:09, Larry Garfield wrote:
> On Fri, Aug 22, 2025, at 12:45 PM, Kyle Katarn wrote: > > > About "What determines comparability", it follows the usual rules of > > PHP: https://www.php.net/manual/en/language.operators.comparison.php > > > > So it's equivalent to ($value < $min) ? $min : (($value > $max) ? $max > > : $value) and also equivalent to min($max, max($min, $value)) > > > > About clamp(new Point(1, 2), new Point(0, 0), new Point(5, 5)); > > > > If Point is a comparable value (simple DTO for example), it should > > return $value, like when doing ($value < $min) ? $min : (($value > > > $max) ? $max : $value) we could add a test for such case, but I think > > that for consistency, whatever currently works in min() should work in > > clamp() > > > > Following the link of the implementation, there is also a link to the > > documentation where I already explained the comparison rules following > > the example of what was done in the documentation for min() and max(): > > https://github.com/php/doc-en/pull/4814 > > Please make sure the above is captured in the RFC. > > Though apparently what PHP currently does with Point comparisons in min/max is... weird. I don't even know what the logic here is. :) > > https://3v4l.org/pTmiV > > (No need to change it in this RFC, just note explicitly that the expected behavior is identical to that min(max()) construct, regardless of type.) > > --Larry Garfield >
It only compares the first property, and if there is a conflict, it moves on to the next property. That is why some of us have been advocating for operator overrides: because this default isn’t always appropriate, as your Point example illustrates. In some cases, you would want to compare magnitude or something else entirely, not just the x value. What is even trickier is that this process completely ignores computed hooks. So, if you add a hook as the first property (for example, returning the magnitude), it is simply ignored. As a weird side note: if you cast the object to an array, the computed hook disappears, but if you use `json_encode()`, the computed value is included. To complicate things further, the current behaviour for objects is actually “undefined” and it isn’t documented. There are examples that show how objects are compared, but they don’t explicitly mention that the object is being cast to an array (see the above paragraph on why that matters). Because this relies on implementation details rather than documented behaviour, it could change between PHP versions without an RFC. That makes relying on it a bit risky. Adding operator overrides would help by making this behaviour explicit and consistent across versions, or just defining it in general. But whenever someone tries to define this more clearly (as I started to do with Records), it tends to run into resistance on this list for reasons I’ve never quite understood. — Rob

Kyle Katarn

1 year ago
2025-08-23 at 12:06, Rob Landers <rob@bottled.codes> wrote:
> > On Fri, Aug 22, 2025, at 22:09, Larry Garfield wrote: > > On Fri, Aug 22, 2025, at 12:45 PM, Kyle Katarn wrote: > > > About "What determines comparability", it follows the usual rules of > > PHP: https://www.php.net/manual/en/language.operators.comparison.php > > > > So it's equivalent to ($value < $min) ? $min : (($value > $max) ? $max > > : $value) and also equivalent to min($max, max($min, $value)) > > > > About clamp(new Point(1, 2), new Point(0, 0), new Point(5, 5)); > > > > If Point is a comparable value (simple DTO for example), it should > > return $value, like when doing ($value < $min) ? $min : (($value > > > $max) ? $max : $value) we could add a test for such case, but I think > > that for consistency, whatever currently works in min() should work in > > clamp() > > > > Following the link of the implementation, there is also a link to the > > documentation where I already explained the comparison rules following > > the example of what was done in the documentation for min() and max(): > > https://github.com/php/doc-en/pull/4814 > > Please make sure the above is captured in the RFC. > > Though apparently what PHP currently does with Point comparisons in > min/max is... weird. I don't even know what the logic here is. :) > > https://3v4l.org/pTmiV > > (No need to change it in this RFC, just note explicitly that the expected > behavior is identical to that min(max()) construct, regardless of type.) > > --Larry Garfield > > > It only compares the first property, and if there is a conflict, it moves > on to the next property. That is why some of us have been advocating for > operator overrides: because this default isn’t always appropriate, as your > Point example illustrates. In some cases, you would want to compare > magnitude or something else entirely, not just the x value. > > What is even trickier is that this process completely ignores computed > hooks. So, if you add a hook as the first property (for example, returning > the magnitude), it is simply ignored. As a weird side note: if you cast the > object to an array, the computed hook disappears, but if you use > json_encode(), the computed value is included. > > To complicate things further, the current behaviour for objects is > actually “undefined” and it isn’t documented. There are examples that show > how objects are compared, but they don’t explicitly mention that the object > is being cast to an array (see the above paragraph on why that matters). > Because this relies on implementation details rather than documented > behaviour, it could change between PHP versions without an RFC. That makes > relying on it a bit risky. > > Adding operator overrides would help by making this behaviour explicit and > consistent across versions, or just defining it in general. But whenever > someone tries to define this more clearly (as I started to do with > Records), it tends to run into resistance on this list for reasons I’ve > never quite understood. > > — Rob >
I would not be against operator overrides, though it starts becoming quite complex when dealing with comparing 2 objects of different classes. Anyway I will add a note on the RFC about objects to warn about unexpected behavior, but this is probably out of scope, the same concerns apply to min() and max() and that's why I added the same warning in the documentation: "Be careful when passing arguments of different types because clamp can produce unpredictable results." Adding a specific note about objects in the documentation could make sense. Then it would probably be nice if it were also added to min() and max().

Christoph Becker

1 year ago
On 22.08.2025 at 10:01, Kyle Katarn wrote:
> > 2025-08-20 at 14:28, Christoph M. Becker <cmbecker69@gmx.de> wrote : > >> I've granted RFC karma to the kylekatarnls account; there is also a >> kylek account, though? Can that be removed? > > Thanks, yes "kylek" account can be removed.
Done. :)

Tim Düsterhus

1 year ago
Hi Am 2025-08-22 10:01, schrieb Kyle Katarn:
> I created https://wiki.php.net/rfc/clamp_v2 (re-using most of the info > from > https://wiki.php.net/rfc/clamp).
I'm noting that the status of the RFC still is Draft and it's also listed in the “Draft” section of the RFC overview. I'm seeing that some folks already discussed the contents, but did you mean for the RFC already being ready for discussion or are some parts still missing? If it's ready you should update the status [1]. Best regards Tim Düsterhus [1] Strictly speaking it would also need a fresh mailing list thread, but I guess using this one is fine if the discussion runs for at least 14 days from when you update the status and announce the official start of the discussion period.