rfc:undefined_property_error_promotion

PHP RFC: Undefined Property Error Promotion

  • Version: 1
  • Date: 2022-04-04
  • Author: Mark Randall, marandall@php.net
  • Status: Accepted

Introduction

Undefined properties are those that have not yet been defined either by the presence of a property declaration, or by adding them to the properties hashmap through direct assignment, or by having them assigned by an internal function such as json_decode. Accessing an undefined property emits an E_WARNING “Undefined property <className>::$varName” and treats the value as if it were null, but does not otherwise interupt execution, allowing code execution to continue unabated, but potentially in an unintended state.

Undefined properties previously triggered an E_NOTICE, however this was upgraded to E_WARNING as part of the PHP 8.0 Engine Warnings RFC: https://wiki.php.net/rfc/engine_warnings - By the time this RFC would come into effect, if passed, undefined properties would have emitted a warning for 5+ years.

Proposal

This RFC proposes that accessing an undefined property is rendered illegal behaviour in the next major version of PHP, and will result in an error exception being thrown if it occurs.

For the purposes of this RFC, accessing an property means to use the property in such a way that the engine attempts to read its value for use in an expression, without accounting for the possibility of it being unset. These can be identified by the warning Undefined property <className>::$varName.

isset / empty / null coalesce DO account for undefined properties and as such are not covered by this RFC.

The presence of magical `get` will continue to work as it does now, and unknown properties will route to the magical `get` handler rather than cause an error (unless called within `get` on itself).

Considerations

PHP has traditionally been quite lax in its enforcement of accessing undefined values that are stored within various data structures.

There are 3 primary storage locations for values within PHP, these are:

  1. Variables (compiled variables) which are stored within the stack frame.
  2. Properties which are stored within a zend_object instance within the properties HashTable and the properties_table (which identifies their location by their slot number sourced from the class entry).
  3. Arrays stored within the bucket.

With the passing of the undefined variable error promotion RFC (https://wiki.php.net/rfc/undefined_variable_error_promotion) the first of these, variables, is scheduled for promotion to an error in PHP 9.0.

The next natural target is properties, this is in line in line with additional changes targeted at PHP 9.0 that will promote WRITING to undefined properties to be an Error (https://wiki.php.net/rfc/deprecate_dynamic_properties) unless the #[AllowDynamicProperties] attribute is set upon the class definition (stdClass includes this capability as standard).

It seems to be the natural progression that if writing to an undefined property will throw, reading from one should do likewise, to protect the user against unintended behaviour.

However, there is a potential exception to this rule, and that is the presence of stdClass, which is intended to exclusively hold unknown (to the class entry) properties, often those sourced from casting an array to an object, or from an internal function decoding some other data such as json_decode when it encounters an object, without the assoc parameter set.

While reading an undefined property on a user defined class will almost always be programmer error, historically it would not be entirely unexpected to see similar to the following code when dealing with json:

if ($obj->name) { 
  echo "Hello " . $obj->name;
}

As this now emits an E_WARNING it should be very much discouraged, but will still execute without an error as $obj->name will, if undefined, evaluate to null.

If this RFC were to pass, the developer would need to explicitly specify their intent to allow the possibility of accessing an undefined property, either via isset, empty or the null coalesce operator:

if (isset($obj->name)) { 
  echo "Hello " . $obj->name;
}

To provide the most consistent handling, this RFC proposes that stdClass will also throw an error if an undefined property is accessed.

Backward Incompatible Changes

Accessing an undefined property in a manner that currently results in an “undefined property” warning will in future result in an Error exception being thrown.

Proposed PHP Version(s)

This change is targeted for PHP 9.0.

Although the target version is mandated by our traditional breaking changes policy, it is also the intent of this RFC to give multiple years of notice that this change will be coming, affording the greatest opportunity for developers to modify their code in anticipation of this change.

A minor change will be included in the next minor version to alter the existing warning message to indicate the warning will become an error in 9.0.

Voting

Voted started on 2022-04-22, ending 2022-05-05

Promote Undefined Property Access to Throw an Error?
Real name Yes No
asgrim (asgrim)  
ashnazg (ashnazg)  
beberlei (beberlei)  
bmajdak (bmajdak)  
brzuchal (brzuchal)  
bwoebi (bwoebi)  
colinodell (colinodell)  
crell (crell)  
cschneid (cschneid)  
derick (derick)  
dharman (dharman)  
diegopires (diegopires)  
ekin (ekin)  
galvao (galvao)  
gasolwu (gasolwu)  
geekcom (geekcom)  
girgias (girgias)  
imsop (imsop)  
kalle (kalle)  
kelunik (kelunik)  
kocsismate (kocsismate)  
lbarnaud (lbarnaud)  
marandall (marandall)  
nicolasgrekas (nicolasgrekas)  
ocramius (ocramius)  
pierrick (pierrick)  
pollita (pollita)  
reywob (reywob)  
santiagolizardo (santiagolizardo)  
sergey (sergey)  
svpernova09 (svpernova09)  
thekid (thekid)  
theodorejb (theodorejb)  
timwolla (timwolla)  
trowski (trowski)  
weierophinney (weierophinney)  
Final result: 31 5
This poll has been closed.

Meta vote:

Main reason for voting against undefined_property_error_promotion if you did?
Real name Backwards compatibility breaks Would be in favour, but not in 9.0 Do not like stdClass behaviour Something else
brzuchal (brzuchal)    
bwoebi (bwoebi)    
cschneid (cschneid)    
derick (derick)    
girgias (girgias)    
imsop (imsop)    
reywob (reywob)    
Final result: 0 0 6 1
This poll has been closed.
rfc/undefined_property_error_promotion.txt · Last modified: 2022/05/06 18:22 by marandall