[PHP-DEV] RFC: Marking return values as important (#[\NoDiscard])

On Wed, Feb 12, 2025, at 17:43, Tim Düsterhus wrote:

Hi

Am 2025-02-12 15:07, schrieb Kamil Tekiela:

I’d be honest, I have a very negative attitude towards this proposal

and I’d be voting against it. It seems to me like it’s creating a

problem and then trying to find a solution for it.

Given that even Rust with a modern stdlib and ergonomic language

features has such an attribute, we do not believe it’s an invented

problem. With the bulk_process() example, we also tried to provide

coherent user-story with a use-case that the readers might’ve already

encountered themselves in the same or a similar fashion. Personally I

encountered situations where I wished such an attribute would’ve warned

me both in PHP and JavaScript/TypeScript. Also in C of course, but given

that C doesn’t have Exceptions, the situation is somewhat different.

For an additional example use-case, consider a function returning a

resource object with side-effects which will automatically clean up the

resource when being destructed. Specifically a function to spawn a new

process or thread:

class Process {

public function __destruct() { /* kill the process */ }

}

#[NoDiscard("the process will be terminated when the Process object

goes out of scope")]

function start_process(string $executable): Process {

return new Process($executable);

}

start_process(‘/my/background/process’);

Depending on timing, the process might or might not run to completion

before PHP gets around to kill it, for example when a step debugger is

attached to PHP, making the bug disappear when debugging.

You yourself has said this will behave differently depending on whether or not opcache is available. Would that not make the warning disappear in dev (where opcache and xdebug are not usually compatible) only to show up in production, potentially causing a worse situation for those throwing warnings as exceptions?

A return value is always supposed to be used. If some API is returning

a value that can be safely ignored, it’s a badly designed API. If a

We agree that in a greenfield ecosystem, we would make ignoring the

return value of a function a warning by default (with an opt-out

mechanism), as done in Swift. However in PHP we do not have a greenfield

situation, there a number of functions where the return value is useless

and only exists for historical reasons. This includes all functions

which nowadays have a return-type of just true, which was only added

for this singular purpose.

There will eventually be a php 9, where BC changes will be possible.

There’s cases where ignoring the return value is safe and reasonable,

without being a badly designed API. array_pop() would come to mind: If

one is only interested in the side-effect of removing the last element

in the array, one does not need the return value without necessarily

doing something unsafe.

I have to stop you here. It is absolutely NOT safe and reasonable to ignore the output of array_pop. You popped it for a reason. If you just care about removing the last element, then you should use unset. Unset gives the intention. If I review code with array_pop, I’ll spend quite a bit of time checking that it was intentionally ignoring the return value (and leave a comment of the same, asking to use unset instead).

doesn’t feel like the solution. In fact, it’s creating a problem for

users who want to ignore the value, which you then propose to solve

with (void) cast.

Ignoring the return value of functions having the attribute should be a

rare occurrence given the intended use-case of pointing out unsafe

situations. But as with static analyzers there needs to solution to

suppress warnings, after carefully verifying that the warning is not

applicable in a given situation.

And what if it isn’t? There’s a non-zero possibility we will be seeing RFCs for years arguing over whether one function or another should have this attribute (like array_pop), not to mention libraries using it as well.

the compilation phase. In PHP warnings are runtime errors. The code

should emit an exception instead of a warning. It would also make it

See Volker’s reply to Derick.

much easier to handle and you wouldn’t need any special construct to

allow users to ignore the new attribute. And I am really not a fan of

Even if an Exception would be thrown, there would be a mechanism to

suppress the Exception. A catch-block wouldn’t work, because if the

Exception is thrown, the function is not executed / control flow is

interrupted.

Many people turn warnings into exceptions. I’m not a fan, personally, but the codebase I work in daily does this.

the PHP engine generating E_USER_WARNING which should be reserved only

for warnings triggered by trigger_error.

This follows the lead of the #[\Deprecated] attribute, which emits

E_USER_DEPRECATED for userland functions and E_DEPRECATED for native

functions, despite being triggered by the engine.

The examples you used don’t support the need for the new attribute.

Regarding the DateTimeImmutable methods, you said yourself: "The

methods are badly named, because they do not actually set the updated

value". So your proposal suggests adding a brand new thing to PHP to

deal with bad method names?

DateTimeImmutable is not part of the “Use Cases” section: Our intended

use-case is the kind of bulk_process() functionality that is used for

all the code snippets. But given that the attribute is also useful for

DateTimeImmutable, we made it part of the proposal, without it being

part of the intended “user-story”.

This problem should be solved using static analysers, IDE, and proper

code refactoring.

See Volker’s reply to Matteo.

Best regards

Tim Düsterhus

— Rob

On Wed, Feb 12, 2025, at 5:52 AM, Volker Dusch wrote:

On Wed, Jan 29, 2025 at 4:12 PM Tim Düsterhus <tim@bastelstu.be> wrote:

Volker and I would like to start discussion on our RFC to allow "Marking
return values as important (#[\NoDiscard])".

Please find the following resources for your reference:

- RFC: PHP: rfc:marking_return_value_as_important
- Implementation: RFC: Marking return values as important (#[\NoDiscard]) by TimWolla · Pull Request #17599 · php/php-src · GitHub

Hello everyone,

it's been two weeks, but given the feedback we received, we don't feel
the discussion didn't reach a conclusive resolution yet.

So we wanted to solicit additional opinions. In an attempt to summarize
the discussion, how we see it, there are two main points of contention
we'd like to discuss further.

I'm still undecided on the RFC overall, but one thing that is problematic is the phrasing of the messages. Currently, the messages in the attribute are fragments of an English sentence, seemingly designed to fit grammatically with a sentence fragment that is coded into the engine somewhere but not readily available to developers.

From my phrasing I think you can guess my opinion of that.

That is impossible to document cleanly for English speakers. It will not translate at all for anyone who is writing in a non-English language (which people do). People are going to get it wrong more than they get it right, in any language.

Instead, the wording should be structured to be a complete sentence, and the built-in message updated to make that logical. That gives the developer much more freedom to write a meaningful, contextually-relevant message in the language of their choice.

--Larry Garfield

I must agree with Kamil. I don’t see practical benefits of this feature that would surpass the implications it has for the language. We already have static analysis handling such cases and it can be extended even to non-pure functions. Moreover, syntax (void) adds additional complexity to beginners’ understanding of the typing system. The second option with $_ is BC on the other hand, so both options are unsatisfying. And honestly, I don’t have good ideas for it.

Kind regards,

Jorg Sowa

On 2025-02-13 00:52, Volker Dusch wrote:

b) Naming of the attribute

Nobody mentioned this on the list, but before opening a vote we'd like to heard if the attribute name makes sense to you.

On this, it could be spelled #[\MustUse], rather than phrase it as a negative.

My thoughts overall on this:

  1. I’m not against introducing the attribute, only how it’s used/enforced etc. (And, incidentally, what it actually MEANS, see if you can spot that from the rest of my comments…)

  2. Static analysers and IDEs: I agree with everyone who’s said that whether or not the return value is “used” should be a concern for tooling like static analysers and the IDE, and ideally not for PHP runtime.
    Attributes were added as a structured replacement for docblock props and I don’t like it when they affect how a program actually runs (as long as you’re not using reflection).

  3. If it must be a concern for PHP at runtime, a warning is preferred over an exception. It only makes sense – it’s in the name: Exceptions are for exceptional situations, warnings are for warning you.

  4. Naming of the attribute: I think the most precise name (if I understand the purpose correctly) is: #[ReturnValueMayContainCriticalInformation]

  5. Should all internal functions, such as fopen() (which may return false on error instead of throwing an exception), have this attribute?
    I understand that ideally we only want to add it where the failure to inspect the return value could result in something outright dangerous, but I think that’s a really hard call to make.
    If we scale it back to mean “Look, this function may not behave the way you’d expect it to, - the return value may actually contain some information you might be interested in, so you better check it man”, then I think it could be a small but useful addition.

Best,
Jakob

Hello, everyone

I’m just wondering how the new attribute that defines behavior (not just additional metadata) will fit into the rest of the system.

Right now, return type hints are not implemented just as an attribute, but as “native” type declaration.

I mean, what we have right now:

function foo(): int {}

could as well have been implemented as:

#[ReturnType(‘int’)]
function foo() {}

Yet, the native type declarations that we have is more concise and fits better.

Do you think it’s reasonable to implement “non-discardability of the returned value” as the attribute? Maybe new keyword would be better solution?

On Wed, Jan 29, 2025 at 4:14 PM Tim Düsterhus <tim@bastelstu.be> wrote:

Hi

Volker and I would like to start discussion on our RFC to allow “Marking
return values as important (#[\NoDiscard])”.

Please find the following resources for your reference:

Best regards
Tim Düsterhus

I don’t see this as something useful when you want to force interactions with variables. I do see value in using this for the keeping and closing of resources, but the only error you should ever get is when you use #[NoDiscard] without a return value.

https://3v4l.org/L4bOc

<?php #[NoDiscard] function lockFile(string $path) { $handle = fopen($path, 'r+'); if (! $handle || ! flock($handle, LOCK_EX)) { throw new RuntimeException('Failed locking ' . $path); } return $handle; } // what we write function main() { lockFile($path); doSomething(); echo 'hi'; } // gets rewritten internally to something like function main() { $hidden = lockFile($path); doSomething(); echo 'hi'; unset($hidden); } // if we do assign it, we omit it all // what we write here would not get rewritten function main() { $handle = lockFile($path); doSomething(); echo 'hi'; } // if we were to ever get a "using"-like // what we write function main() { using { lockFile($path); doSomething(); } echo 'hi'; } // gets rewritten internally to something like function main() { $hidden = lockFile($path); doSomething(); unset($hidden); echo 'hi'; }

Hi

Am 2025-02-12 22:31, schrieb Larry Garfield:

I'm still undecided on the RFC overall, but one thing that is problematic is the phrasing of the messages. Currently, the messages in the attribute are fragments of an English sentence, seemingly designed to fit grammatically with a sentence fragment that is coded into the engine somewhere but not readily available to developers.

Yes, the implementation of the error message very closely matches that of #[\Deprecated] (except that there is no `since` bit to insert).

From my phrasing I think you can guess my opinion of that.

That is impossible to document cleanly for English speakers. It will not translate at all for anyone who is writing in a non-English language (which people do). People are going to get it wrong more than they get it right, in any language.

Instead, the wording should be structured to be a complete sentence, and the built-in message updated to make that logical. That gives the developer much more freedom to write a meaningful, contextually-relevant message in the language of their choice.

We're open to adjusting that. Do you have any suggestions? The implementation of #[\Deprecated] works like it does, because PHP itself already doesn't end the error messages with a `.`, as it appends `in file.php on line 42`. This makes it inconvenient to have more than one sentence in an error message, which is why we're struggling with coming up with something better.

Best regards
Tim Düsterhus

Hi

Am 2025-02-13 09:16, schrieb Jakob Givoni:

Attributes were added as a structured replacement for docblock props and I
don't like it when they affect how a program actually runs (as long as
you're not using reflection).

Excluding the `#[\Attribute]` attribute, PHP currently has 5 native attributes and they all affect how the program runs. The initial accepted Attribute RFC even lists several “behavior-affecting” attributes in the “Use Cases” section: PHP: rfc:attributes_v2. It is probably fair to say that use-cases like `#[\NoDiscard]` do not go against the vision intended by the Attribute RFC.

You could think of it as the PHP engine using Reflection internally to do something differently.

3. Naming of the attribute: I think the most precise name (if I understand
the purpose correctly) is: #[ReturnValueMayContainCriticalInformation]

Yes. Or perhaps `#[\ReturnValueImportant]` as used by the RFC title to keep it a little more succinct.

If we scale it back to mean "Look, this function may not behave the way
you'd expect it to, - the return value may actually contain some
information you might be interested in, so you better check it man", then I
think it could be a small but useful addition.

Let me refer to the “Recommended Usage” section of the RFC: PHP: rfc:marking_return_value_as_important

My rule of thumb would be that if you have trouble writing a good custom explanation in the attribute, then it's likely not a good fit.

Best regards
Tim Düsterhus

Hi

Am 2025-02-13 09:49, schrieb Eugene Sidelnyk:

I'm just wondering how the new attribute that defines behavior (not just
additional metadata) will fit into the rest of the system.

See my reply to Jakob: There are already several attributes that define behavior.

Do you think it's reasonable to implement "non-discardability of the
returned value" as the attribute? Maybe new keyword would be better
solution?

Yes, we believe that implementing this as an attribute rather than a keyword is the right choice and the reasoning is similar to the `#[\Override]` attribute that was added in PHP 8.3: PHP: rfc:marking_overriden_methods

With the exception of error handlers, applying (or unapplying) the `#[\NoDiscard]` attribute will not affect how a PHP program works. It will just emit a new warning which when suppressed, will not affect the program’s behavior at all. This is different to return types, which are relevant due to the Liskov substitution principle and where changing the return type would actually result in a breaking change to the program’s public API.

Best regards
Tim Düsterhus

On Thu, Feb 13, 2025, at 8:16 AM, Tim Düsterhus wrote:

Hi

Am 2025-02-12 22:31, schrieb Larry Garfield:

I'm still undecided on the RFC overall, but one thing that is
problematic is the phrasing of the messages. Currently, the messages
in the attribute are fragments of an English sentence, seemingly
designed to fit grammatically with a sentence fragment that is coded
into the engine somewhere but not readily available to developers.

Yes, the implementation of the error message very closely matches that
of #[\Deprecated] (except that there is no `since` bit to insert).

From my phrasing I think you can guess my opinion of that.

That is impossible to document cleanly for English speakers. It will
not translate at all for anyone who is writing in a non-English
language (which people do). People are going to get it wrong more than
they get it right, in any language.

Instead, the wording should be structured to be a complete sentence,
and the built-in message updated to make that logical. That gives the
developer much more freedom to write a meaningful,
contextually-relevant message in the language of their choice.

We're open to adjusting that. Do you have any suggestions? The
implementation of #[\Deprecated] works like it does, because PHP itself
already doesn't end the error messages with a `.`, as it appends `in
file.php on line 42`. This makes it inconvenient to have more than one
sentence in an error message, which is why we're struggling with coming
up with something better.

Best regards
Tim Düsterhus

Just spitballing:

"Return value of foo() is not used, on foo.php line 5: <user text here>"

Fiddle with the wording as needed, but by having a colon and then the user text, it makes it clear it's a separate statement, and can be as flexible as a statement in your chosen language wants to be.

--Larry Garfield

On Thu, Feb 13, 2025 at 3:17 PM Tim Düsterhus <tim@bastelstu.be> wrote:

Hi

Am 2025-02-13 09:16, schrieb Jakob Givoni:

Attributes were added as a structured replacement for docblock props
and I
don’t like it when they affect how a program actually runs (as long as
you’re not using reflection).

Excluding the #[\Attribute] attribute, PHP currently has 5 native
attributes and they all affect how the program runs. The initial
accepted Attribute RFC even lists several “behavior-affecting”
attributes in the “Use Cases” section:
https://wiki.php.net/rfc/attributes_v2#use_cases. It is probably fair to
say that use-cases like #[\NoDiscard] do not go against the vision
intended by the Attribute RFC.

You could think of it as the PHP engine using Reflection internally to
do something differently.

I think all the native attributes so far, with the exception of #[\AllowDynamicProperties],
only affect the program at compile-time, or by emitting errors.
They don’t affect the program otherwise by changing behavior,
throwing exceptions etc. (As long as you don’t use reflection, nor promote errors to exceptions.)

AllowDynamicProperties is the only one that may change the program behavior
at run-time by throwing an exception if the property is removed from a class while running PHP versions 9.0+.
So this is not even a reality yet, since PHP 9 is not yet released.
And I think it sets a bad precedent. Perhaps AllowDynamicProperties should have been a
language construct instead. Or only fail at compile time.

The original attributes RFC does mention “runtime behavior” in the introduction,
but none of the use-cases proposed showcase it without using reflection or
promoting errors to exceptions (as far as I can see).

I don’t mean to be rude and use your own words against you, but I found this quote in the “Why an attribute and not a keyword?” section of your #[\Override] RFC:

This RFC proposes an attribute instead of a keyword, because contrary to other modifiers (e.g. visibility) that are part of the method signature, the attribute does not affect behavior or compatibility for users that further extend a given class and neither does it affect users that call the method. It is purely an assistance to the author of a given class.

(and I apologize if I misunderstand your intention or take it out of context completely), but
I like the sentiment that “the attribute does not affect behavior or compatibility for users”
and that it’s part of the argument for an attribute instead of a keyword (language construct).

I feel like #[NoDiscard] is getting dangerously close to being part of a method signature,
especially if it throws exceptions at run-time (which I know it doesn’t do in your RFC, so I’m less against it).

I might be the only “purist” here, but just thought I would highlight that at least one php developer
is hesitant with the direction attributes might be taking :slight_smile:

Best,
Jakob

On Wed, 12 Feb 2025, Volker Dusch wrote:

On Wed, Jan 29, 2025 at 4:12 PM Tim Düsterhus <tim@bastelstu.be> wrote:

> Volker and I would like to start discussion on our RFC to allow "Marking
> return values as important (#[\NoDiscard])".
>
> Please find the following resources for your reference:
>
> - RFC: PHP: rfc:marking_return_value_as_important
> - Implementation: RFC: Marking return values as important (#[\NoDiscard]) by TimWolla · Pull Request #17599 · php/php-src · GitHub
Hello everyone,

it's been two weeks, but given the feedback we received, we don't feel
the discussion didn't reach a conclusive resolution yet.

So we wanted to solicit additional opinions. In an attempt to
summarize the discussion, how we see it, there are two main points of
contention we'd like to discuss further.

a) Intentionally discarding the return value

The `(void)flock($fp);` syntax was the main thing discussed on the list.
With `$_ = flock($fp);` as the most suggested alternative.

The issue with using `$_` as it currently stands, is that the unused
variable would be cleaned up by OPcache. Should we instead have a
special case in OPcache ensuring it does not apply its optimization to
a variable if it's named `$_` instead? The semantic difference would
be that $_ would keep returned values (and objects) alive and in
memory, whereas (void) would discard them immediately. Leading to
different times when, for example, destructors would be called.

The consequence of this would be that 3rd party tools like IDEs and
Static and Dynamic Code Analyzers would also have to build that
special case in to not produce "unused variable" warnings.

We're also happy to change the secondary vote to "(void) vs $_" as we
feel there needs to be a way to discard the return value consistently
for this to be a complete and usable feature.

I would not be in favour of special casing this, and henceforth the
(void) cast makes more sense to me. Mostly, because the special casing
has to be done in many places, and in many tools.

b) Naming of the attribute

Nobody mentioned this on the list, but before opening a vote we'd like
to heard if the attribute name makes sense to you.

We've chosen #[NoDiscard] as it's also used in C and C++. See [1] for
the full list of references. If you feel this doesn't fit with PHP, we
welcome other suggestions.

I think it fits, and there is already precedence.

cheers,
Derick

--
https://derickrethans.nl | https://xdebug.org | https://dram.io

Author of Xdebug. Like it? Consider supporting me: Xdebug: Support

mastodon: @derickr@phpc.social @xdebug@phpc.social

On Thu, 13 Feb 2025, Larry Garfield wrote:

On Thu, Feb 13, 2025, at 8:16 AM, Tim Düsterhus wrote:
> Hi
>
> Am 2025-02-12 22:31, schrieb Larry Garfield:
>> I'm still undecided on the RFC overall, but one thing that is
>> problematic is the phrasing of the messages. Currently, the messages
>> in the attribute are fragments of an English sentence, seemingly
>> designed to fit grammatically with a sentence fragment that is coded
>> into the engine somewhere but not readily available to developers.
>
> Yes, the implementation of the error message very closely matches that
> of #[\Deprecated] (except that there is no `since` bit to insert).
>
>> From my phrasing I think you can guess my opinion of that.
>>
>> That is impossible to document cleanly for English speakers. It will
>> not translate at all for anyone who is writing in a non-English
>> language (which people do). People are going to get it wrong more than
>> they get it right, in any language.
>>
>> Instead, the wording should be structured to be a complete sentence,
>> and the built-in message updated to make that logical. That gives the
>> developer much more freedom to write a meaningful,
>> contextually-relevant message in the language of their choice.
>
> We're open to adjusting that. Do you have any suggestions? The
> implementation of #[\Deprecated] works like it does, because PHP itself
> already doesn't end the error messages with a `.`, as it appends `in
> file.php on line 42`. This makes it inconvenient to have more than one
> sentence in an error message, which is why we're struggling with coming
> up with something better.

Just spitballing:

"Return value of foo() is not used, on foo.php line 5: <user text
here>"

Fiddle with the wording as needed, but by having a colon and then the
user text, it makes it clear it's a separate statement, and can be as
flexible as a statement in your chosen language wants to be.

PHP always adds "in file.php on line 42" to the end, so that wouldn't
work.

cheers,
Derick

On Thu, 13 Feb 2025, Tim Düsterhus wrote:

Am 2025-02-13 09:16, schrieb Jakob Givoni:

> Attributes were added as a structured replacement for docblock props
> and I don't like it when they affect how a program actually runs (as
> long as you're not using reflection).

Excluding the `#[\Attribute]` attribute, PHP currently has 5 native
attributes and they all affect how the program runs. The initial
accepted Attribute RFC even lists several “behavior-affecting”
attributes in the “Use Cases” section:
PHP: rfc:attributes_v2. It is probably fair
to say that use-cases like `#[\NoDiscard]` do not go against the
vision intended by the Attribute RFC.

You could think of it as the PHP engine using Reflection internally to
do something differently.

I don't agree, but for a different reason.

None of the current attributes (ReturnTypeWillChange,
AllowDynamicProperties, SensitiveParameter, Override, and Deprecated)
change the behaviour of how a program runs. They only add warnings. with
the exception of AllowDynamicProperties to be an actual 'feature' in PHP
9.0 (now it's only a deprecation warning silencer).

That's the same with this suggested NoDiscard, it doesn't change how a
program is run — it merely adds a warning.

cheers,
Derick

On 14.02.2025 12:57, Derick Rethans wrote:

None of the current attributes (ReturnTypeWillChange,
AllowDynamicProperties, SensitiveParameter, Override, and Deprecated)
change the behaviour of how a program runs. They only add warnings. with
the exception of AllowDynamicProperties to be an actual 'feature' in PHP
9.0 (now it's only a deprecation warning silencer).

For clarity, it's not AllowDynamicProperties attribute throwing an exception in PHP9, it's use of a "dynamic" property on an object not marked with this attribute. So, I'm not sure it's a valid exception to the rule.

--
Aleksander Machniak
Kolab Groupware Developer [https://kolab.org]
Roundcube Webmail Developer [https://roundcube.net]
----------------------------------------------------
PGP: 19359DC1 # Blog: https://kolabian.wordpress.com

On Fri, Feb 14, 2025 at 1:10 PM Aleksander Machniak <alec@alec.pl> wrote:

On 14.02.2025 12:57, Derick Rethans wrote:

None of the current attributes (ReturnTypeWillChange,
AllowDynamicProperties, SensitiveParameter, Override, and Deprecated)
change the behaviour of how a program runs. They only add warnings. with
the exception of AllowDynamicProperties to be an actual ‘feature’ in PHP
9.0 (now it’s only a deprecation warning silencer).

For clarity, it’s not AllowDynamicProperties attribute throwing an
exception in PHP9, it’s use of a “dynamic” property on an object not
marked with this attribute. So, I’m not sure it’s a valid exception to
the rule.

You’re absolutely right that it’s not the use of the property that causes exceptions
to be thrown, but it’s still a valid exception to the rule, since the use of the property
changes how a program runs (which was the point that was being made).

Best,
Jakob

Hi

Apologies for the delay in getting back to you. Volker was out of office and I try to coordinate the replies with him to not misrepresent his opinion.

Am 2025-02-12 14:17, schrieb Bilge:

Apologies if this has already been brought up; I haven't read the entire thread, but isn't the entire premise of this RFC based on a falsehood?

We do not believe so.

This kind of “partial success” can only be reliably communicated by means of a return value

Exceptions are objects, so you can attach whatever additional information you wish to that object.

Perhaps the word "reliably" is doing a lot of heavy lifting in that sentence, but if you would refute me (which presumably you will), then I think this needs to be explained in better detail in the RFC, because it looks to me that exploiting object properties is just as viable as returning something, and probably preferred, since this solves your problem of the user not handling the failure.

Perhaps one can find a better word than “reliably” there, nevertheless: We disagree that throwing an Exception is an appropriate solution to communicate partial success. While it certainly would ensure that developers can't forget to handle the (partial) failure case, it would also be a very non-idiomatic use of Exceptions. Instead of being able to check `->getMessage()` or `->getCode()` to find out the cause, which is automatically supported by generic Exception-handling mechanisms as provided by every framework, one needs to specifically call a `->getResults()` methods to find out the individual results.

And when it's a generic function where depending on the type of item processed, one is also interested in the “return value” of the successful cases, instead of the binary success/failure option, it would become very unwieldy, requiring the use of a single-statement try-block to handle both the “all successful” and the “at least one failure” case, ending up in a “return value with extra steps” situation.

     try {
         $results = bulk_process($items);
         /* All successful. */
     } catch (BulkProcessingFailures $e) {
         $results = $e->getResults();
         /* At least one failed. */
     }

     foreach ($results as $item => $result) {
         if ($result instanceof SuccessResult) {
             echo "Created item ", $item, " with ID: ", $result->getId(), PHP_EOL;
         } else {
             echo "Failed to create item ", $item, PHP_EOL;
         }
     }

Best regards
Tim Düsterhus

Hi

Am 2025-02-14 10:10, schrieb Jakob Givoni:

I think all the native attributes so far, with the exception of
#[\AllowDynamicProperties],
only affect the program at compile-time, or by emitting errors.
They don't affect the program otherwise by changing behavior,
throwing exceptions etc. (As long as you don't use reflection, nor promote
errors to exceptions.)

AllowDynamicProperties is the only one that may change the program behavior
at run-time by throwing an exception if the property is removed from a
class while running PHP versions 9.0+.

This is false. The `#[\SensitiveParameter]` attribute changes the behavior of argument capturing whenever PHP creates a backtrace. This includes the backtrace added to Exception objects, the `debug_backtrace()` function and the fatal error backtraces added in PHP 8.5 with PHP: rfc:error_backtraces_v2. This is also listed in the backwards compatible changes section of the `#[\SensitiveParameter]` RFC:

https://wiki.php.net/rfc/redact_parameters_in_back_traces#backward_incompatible_changes

2. Custom exception handlers might see objects of class \SensitiveParameterValue, despite the parameter having a different type within the method's signature.

You mentioned “by emitting errors”, but I don't think that this is applicable here, since `#[\SensitiveParameter]` does not emit an error by itself and because it also affects the `debug_backtrace()` function, which is sometimes used for meta-programming.

-

I don't mean to be rude and use your own words against you, but I found
this quote in the "Why an attribute and not a keyword?" section of
your #[\Override] RFC:

This RFC proposes an attribute instead of a keyword, because contrary to
other modifiers (e.g. visibility) that are part of the method signature,
the attribute does not affect behavior or compatibility for users that
further extend a given class and neither does it affect users that call the
method. It is purely an assistance to the author of a given class.

(and I apologize if I misunderstand your intention or take it out of
context completely), but
I like the sentiment that "the attribute does not affect behavior or
compatibility for users"
and that it's part of the argument for an attribute instead of a keyword
(language construct).

I feel like #[NoDiscard] is getting dangerously close to being part of a
method signature,
especially if it throws exceptions at run-time (which I know it doesn't do
in your RFC, so I'm less against it).

The situation with `#[\NoDiscard]` is indeed different to `#[\Override]`, because it affects the *user* of the API rather than the *creator* of it. But as you said, it doesn't throw an Exception by itself, but emits a warning which like other warnings only affects behavior if you specifically decide to do so via a custom error handler. And even if it would throw an Exception, having the Attribute would be “more visible” than an Exception that the function itself throws, since PHP does not implement “checked Exceptions” as in Java and unless the author adds a `/** @throws SomeException */` PHPDoc it's not clear what Exceptions might be thrown (or if any Exceptions are thrown at all).

Given that other languages also implement it as Attribute + Warning, I'd say we're in good company here. In case of C, using the `-Werror' flag for your compiler would then be equivalent to a throwing error handler :slight_smile:

Best regards
Tim Düsterhus

On Wed, Feb 12, 2025 at 9:57 PM Rob Landers rob@bottled.codes wrote:
Hey Rob,

Sorry for writing the mail again, I just noticed I forgot to include the list on my first reply to you, also corrected a mistake in the second paragraph.

There will eventually be a php 9, where BC changes will be possible.

I don’t assume PHP will change the behavior of widely used core functions to throw exceptions in PHP 9 as the BC impact will be colossal, hurting adoption. Or am I misunderstanding you here?

The point that there might be arguments over usage in internal code is why we went with an “only if it is a clear problem, especially when the function has important side effects” policy to avoid this. There are not a too many examples in core, more in userland.

I have to stop you here. It is absolutely NOT safe and reasonable to ignore the output of array_pop. You popped it for a reason. If you just care about removing the last element, then you should use unset. Unset gives the intention. If I review code with array_pop, I’ll spend quite a bit of time checking that it was intentionally ignoring the return value (and leave a comment of the same, asking to use unset instead).

There are plenty of codebases where array_pop is used for this reason. Identifying the last element of a php array. unset($foo[array_key_last($foo)]); is only possible since 7.3 and not widely used. array_pop is also shorter and faster for the same effect (when used on unknown array shapes ofc). The examples are plenty https://github.com/search?q=repo%3AWordPress%2FWordPress%20array_pop&type=code

Regards,
Volker