Background
- What is AtEase? – It is a micro-library developed for MediaWiki as alternative to the native @ operator.
$content = @file_get_contents( $path );
use Wikimedia\AtEase\AtEase; AtEase::suppressWarnings(); $content = file_get_contents( $path ); AtEase::restoreWarnings();
- What does it do? – It surpresses warnings from the PHP engine. You can think of these as console warnings. They are not exceptions or fatal errors, but just additional information emitted from the program.
- When is it useful? – To cleanly handle expected errorrs. It is considered bad practice to check if a file exists before opening it (it is subject to race conditions, and is inefficient, see EAFP). When unhandled, these errors would cause PHPUnit to mark code as unstable, and would pollute production error logs with unhelpful messages. Hence, these can be surpressed in PHP.
- Why did we create AtEase? – There was a serious problem with the @ operator, which is that it not only disabled error messages for warnings, but also disabled error messages for fatal errors/exceptions. This is especially unexpected given that it did not catch or prevent those fatals, so a CLI process or web request woudl still abort fatally, but just not say anywhere why. (See also Coding conventions.)
For example, if you used @file_get_contenst() and then misspelled the function name, there would be no explaination as to why, where or how the process failed. Which, if it happens randomly in production in some rare code paths would be pretty miserable to try and figure out.
What happened
As of PHP 7, fatal errors and stack traces are no longer hidden by the @ operator. (Example: https://3v4l.org/t8i9q).
So?
- There is non-trivial cost and complexity involved in supporting AtEase due to how easy it is to accidentally forget to call AtEase::restoreWarnings();. When that happens, it could go unnoticed for months or years and hide a lot of errors from production.
- Another common mistake is not realising that exceptions may abort the flow between suppressWarnings and restoreWarnings. Which means even if the author wrote the code seemingly right, if there's any way that an indirectly called code path can throw, it can still lead to AtEase and native error_reporting being left in a dirty state, which can cause all kinds of problems. For example, as part of change 590684 it took several days to trace down why a test case was not passing in WMF CI but seemingly passing in isolation.
- It can't be easily used in unit tests due to assertions using exceptions to communicate with PHPUnit. (Example)
- In tight-loops there is non-trivial overhead from using AtEase, which it it sometimes moved to higher level code to ecompas an entire batch, thus tolerate far more potential warnings, which is undesirable. (Note: It is assumed but unconfimed that @ would have less overhead.)
- (Minor) More verbose code. Another library to maintain.
use Wikimedia\AtEase\AtEase; AtEase::suppressWarnings(); $content = file_get_contents( $path ); AtEase::restoreWarnings();
Proposal 1
- Remove the PHPCS rule for Generic.PHP.NoSilencedErrors.Discouraged.
- Update coding conventions to remove any mention of AtEase.
- Update coding conventions to remove discouragement to use @.
$content = @file_get_contents( $path );
Beyond that, we could make a PHPCS rule to sniff against AtEase. This rule would auto-disabled by library-upgrader in all existing repos' phpcs.xml file (given there are existing violations, and is not easily auto-fixed).
For third parties I suggest we keep bundling wikimedia/at-ease for at least one LTS. It is in wide use and I don't think code generally gains anything from removing existing uses. Let's take it easy on the deprecation and migration :)
I'm not sure yet whether run-time deprecation warnings are a good idea for this. Probably not right away, but maybe after that LTS, we can do one final release of AtEase with deprecation warnings, then freeze/archive the library and remove it MW in the next release after that LTS.
Proposal 2
- Keep coding conventions that discourage use of @.
- Update coding conventions that "when error supressing is really needed" to no longer mention AtEase, but instead mention phpcs:ignore.
// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged $content = @file_get_contents( $path );
For third parties I suggest we keep bundling wikimedia/at-ease for at least two LTS releases. It is in wide use and I don't think we should force developers to waste time on these one-line replacements when for most code it would gain basically nothing from that.
See also
- Source code: https://gerrit.wikimedia.org/g/at-ease/
- Docs: https://www.mediawiki.org/wiki/At-ease