This is an old revision of the document!
PHP RFC: Reclassifying engine warnings
- Date: 2019-08-27
- Author: Nikita Popov nikic@php.net
- Status: Draft
- Target Version: PHP 8.0
Introduction
While newly introduced error conditions in the engine typically use Error
exceptions, we have many old error conditions that use inappropriate severity levels for historical reasons. For example, accessing an undefined variable, while being a very severe programming error, only generates a notice. This RFC proposed to reevalute existing error conditions and reclassify their severity level as appropriate.
Proposal
The following table contains a list of errors with notice or warning severity generated in the engine, excluding warnings that are generated by functions which have an optimized opcode implementation.
The table shows both the current error level, as well as the proposed level. A rationale for the proposed change (or non-change) is provided below a group of errors.
Message | Current Level | Proposed Level |
---|---|---|
Undefined variable: %s | Notice | Error exception |
Rationale: In most cases, an undefined variable is a severe programming error. The current low classification of this error is likely a legacy from the Dark Ages of register_globals, where it was more normal to have variables that are not always available. | ||
Attempt to increment/decrement property '%s' of non-object | Warning | Error exception |
Attempt to modify property '%s' of non-object | Warning | Error exception |
Attempt to assign property '%s' of non-object | Warning | Error exception |
Creating default object from empty value | Warning | Error exception |
Rationale: These are all related errors that are generated when a property is accessed on a non-object inside a write context. If the non-object is “truthy” a warning is generated and the operation is ignored, if it is “falsy” an empty stdClass object is created instead. While auto-vivification is a core part of the language for arrays, the same is not the case for objects, and creating a property on a non-object is almost certainly a programming error rather than an intentional action. | ||
Trying to get property '%s' of non-object | Notice | Warning |
Undefined property: %s::$%s | Notice | Warning |
Rationale: The first warning is for the same case as above, but for read contexts. The suggested classification is based on two facts: First, PHP is generally somewhat lenient about reading of “missing” data, and there is likely significant code around there using this fact, so changing this into an exception would be ill-advised. Second, apart from specific cases like JSON data in object representation, available object properties are generally known and fixed and accessing a non-existing property is a severe programming error, similar to accessing an undefinded variable. As such, the current notice classification is too low. | ||
Cannot add element to the array as the next element is already occupied | Warning | Error exception |
Rationale: This error condition occurs when trying to push to an array for which the PHP_INT_MAX key is already used. This error condition practically never occurs outside of specially crafted code, and implies data loss if it does. As such, it is changed into an exception. |
||
Division by zero | Warning | DivisionByZeroError exception |
Rationale: During the development of PHP 7, two possible behaviors for division by zero were considered: Allowing it silently and returning float-point NaN per IEEE 754 semantics (useful for numerical code), or generating an error exception (useful for everything else). In the end we arrived at a compromise that combines the worse of both possibilities, which is to return NaN and generate a warning. This has caused quite a bit of confusion, because that % operation will throw a DivisionByZeroError instead. This proposal suggests to make / behave consistently and also throw DivisionByZeroError. |
||
Indirect modification of overloaded element of %s has no effect | Notice | (Notice) |
Indirect modification of overloaded property %s::$%s has no effect | Notice | (Notice) |
Rationale: These notices occur if __get() or offsetGet() return a non-reference, but are used in a write context. Because our detection of write context has false positives right now, these should remain notices until we can be sure that the diagnostic is always legitimate. |
||
Only arrays and Traversables can be unpacked | Warning | Error exception |
Invalid argument supplied for foreach() | Warning | Error exception |
Cannot unset offset in a non-array variable | Warning | Error exception |
Cannot use a scalar value as an array | Warning | Error exception |
Trying to access array offset on value of type %s | Notice | Warning |
Accessing static property %s::$%s as non static | Notice | ??? |
Object of class %s could not be converted to int/float/number | Notice | ??? |
A non-numeric value encountered | Warning | ??? |
Array to string conversion | Notice | ??? |
A non well formed numeric value encountered | Notice | ??? |
--- | ||
Illegal offset type | Warning | ??? |
Illegal offset type in isset or empty | Warning | ??? |
Illegal offset type in unset | Warning | ??? |
Resource ID#%d used as offset, casting to integer (%d) | Notice | ??? |
Undefined offset: %d | Notice | ??? |
Undefined index: %s | Notice | ??? |
String offset cast occurred | Notice | ??? |
Illegal string offset: %d | Warning | ??? |
Illegal string offset '%s' | Warning | ??? |
Uninitialized string offset: %d | Notice | ??? |
Cannot assign an empty string to a string offset | Warning | ??? |
--- | ||
Only variables should be passed by reference | Notice | ??? |
Only variable references should be returned by reference | Notice | ??? |
Only variable references should be yielded by reference | Notice | ??? |
Only variables should be assigned by reference | Notice | ??? |
Attempting to set reference to non referenceable value | Notice | ??? |
Parameter %d to %s%s%s() expected to be a reference, value given | Warning | ??? |
Cannot pass by-reference argument %d of %s%s%s() by unpacking a Traversable, passing by-value instead | Warning | ??? |
Backward Incompatible Changes
Conversion of noticed to warnings is fairly harmless, because both continue execution after the diagnostic has been generated. Conversion to exceptions implies that the current control flow will be aborted.
This may impact code that makes very liberal use of the error suppression operator @
or disables error reporting wholesale. The proposal does try to avoid changing notices that are more likely to be suppressed into exceptions.
Vote
Due to the number of error conditions involved here, this RFC will be voted as a single proposal, rather than voting on each individual change.