Problem/Motivation
PHP 8.5 introduced #NoDiscard attribute which is helpful for testing and to indicate that a function's return value is important and should be consumed.
Also core already depends on polyfill-php85
Steps to reproduce
See examples in https://www.php.net/manual/migration85.new-features.php#migration85.new-...
Proposed resolution
Use #[NoDiscard] only in the following situations:
- Methods where mistakes in API usage can cause critical bugs. Some examples are security, access control, and encryption methods.
- Methods on value objects which return a new object instead of altering it. Some examples are AccessResult and CacheableMetadata.
Document the decision at Coding practices.
Remaining tasks
User interface changes
Introduced terminology
API changes
Data model changes
Release notes snippet
| Comment | File | Size | Author |
|---|---|---|---|
| #12 | nodiscard.png | 78.78 KB | andypost |
Issue fork drupal-3560672
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
- 3560672-adopt-nodiscard-attribute
changes, plain diff MR !14887
Comments
Comment #2
andypostAs polyfill already added core can use the attribute on earlier versions of PHP
https://github.com/symfony/polyfill-php85
Comment #3
andypostComment #4
joachim commentedI'm not sure what the polyfill serves here, unless elsewhere in Symfony there's magic which detects the #NoDiscard attribute in static code analysis.
Comment #5
longwavePHPStan supports
#[NoDiscard]: https://github.com/phpstan/phpstan/releases/tag/2.1.28Comment #7
joachim commentedWhat else do we need to do here?
Comment #8
andypost@joachim as summary said we need to mark sensitive functions with this attribute so consumers will throw if result is not used
Comment #9
joachim commentedI mean, before we tackle the child issues to actually add the attribute, is there anything else to do here?
Comment #10
andypostMy idea was to apply it to few places like AccessResult, CacheableMetadata, cache merge, validators, query builders, render array builders
I will create MR to give a try asap but let's find more places where it helpful
Comment #12
andypostSomehow PHPStan does not catch it but PHPStorm showing
In
core/modules/media/src/MediaAccessControlHandler.php:130—Comment #13
andypostit works dynamically
Comment #14
andypostPHPStan does not catch because we have
checkThisOnly: trueand I'm not sure it viable to allow it as much more errors appearComment #15
andypostComment #16
andypostSo I think it ready to file child issues and it's enough to add the attribute to interface only
Comment #17
andypostInterface only attribute is not working(
Comment #19
andyposton direct class call PHPStan works https://git.drupalcode.org/issue/drupal-3575572/-/jobs/8642189
Comment #20
andypostFiled few child issues
Comment #22
joachim commentedI'm confused - we're filing child issues, but the MR here seems to be all the changes?
Comment #23
andypostI just tested here different approaches, child issues are to work on
Comment #24
dcam commentedI looked at these issues last night and had a couple of thoughts:
Comment #25
joachim commented> It would be nice for the child issues to include the justification for why the attribute should be applied to the included functions.
Because not using the return of those is probably (or even always!) a mistake. Same reason for all of them really!
Comment #26
dcam commentedI figured that out from reading the docs. But that doesn't mean someone can understand at a glance why that's the case for a particular function or set of functions, especially if they're looking at an API they aren't familiar with.
Comment #27
andypostwould be great to find some better wording then deadcode
Comment #28
andypoststill the most illustrative example is https://git.drupalcode.org/project/drupal/-/merge_requests/14887/diffs?c...
Comment #29
andypostGuideline to reuse result as state got changes after call
Comment #30
longwaveComing from #3575572: Add [#NoDiscard] to Utility classes which added it to multiple classes:
\Drupal\Component\Utility\Color
\Drupal\Component\Utility\Crypt
\Drupal\Component\Utility\Html
\Drupal\Component\Utility\NestedArray
\Drupal\Component\Utility\Tags
To me I don't see the need to add it to Color or Tags, if we go down that route then we could add it to practically any method that doesn't have a void return type, but I'm not sure that's really necessary.
Initially I think this should be only added to security, access control or encryption related methods, where mistakes in API usage can cause critical bugs. However, I'm open to suggestions as to other types of API as well?
Comment #31
joachim commented> Initially I think this should be only added to security, access control or encryption related methods, where mistakes in API usage can cause critical bugs
+1
Though I would add to that, methods on value objects which return a new object instead of altering it.
So: AccessResult and CacheableMetadata, for instance.
Comment #32
penyaskito+1. This is one of the first thoughts I have when thinking about
#[NoDiscard]. E.g.\Drupal\jsonapi\JsonApiResource\LinkCollectionand everywhere there's some kind ofreturn new static(maybe aside of constructors, but maybe we want _also_ constructors).Edit: I added another "non security" example because of AccessResult and CacheableMetadata could (should) even be considered security, access control related methods.
Comment #33
quietone commentedTried to put the when to use this into the proposed resolution
Comment #34
smustgrave commentedBased on the current proposed solution is there consensus those are good guidelines to use this?
Comment #36
smustgrave commentedThe proposed changes makes sense and there's been no follow up since they were added. What would be next?
Comment #37
longwaveWe need to figure out where to document this decision - either in existing documentation somewhere or a new page.
Comment #38
smustgrave commentedMaybe a page under https://www.drupal.org/docs/develop/security ?
Comment #39
quietone commented@smustgrave, that does make sense. But considering we have other Coding Practices I think a more general solution is needed. For a long term solution I think coordination with the coding standards committee is needed.
In the meantime, this issue came up in the committer meeting and catch and I thought the best way to make progress is to make a page in the core policies for this type of topic. The new page is Coding practices.
Comment #40
catchNew page looks great for now, if we find a more permanent place/approach we can move it then. Going to go ahead and mark this fixed.
Comment #42
smustgrave commentedLove the new page and image it has other possibilities!
Comment #43
smustgrave commentedCould it be an announcement?