rfc:json_throw_on_error

PHP RFC: JSON_THROW_ON_ERROR

Introduction

PHP has two functions for dealing with JSON, json_decode() and json_encode(). Unfortunately, both have suboptimal error handling. json_decode() returns null upon erroring, but null is also a possible valid result (if decoding the JSON “null”). It is only possible to know if an error occurred by calling json_last_error() or json_last_error_msg(), which return the global error state in machine-readable and human-readable forms respectively. json_encode() also uses this system, but at least does have a clear error return value. But both functions also do not halt program execution by default on error, or even throw a warning.

This situation is suboptimal, and throwing exceptions would be cleaner: there would be no confusion between error values and correct results, no (possibly outdated) global state, no program execution after the error is thrown unless explicitly handled, and error messages would be neatly traceable to their source. However, to immediately change the default behaviour of these functions to throw would be a significant backwards-compatibility issue, and producing a notice or warning is not ideal for predictable, routine errors that are not bugs or poor code style (as JSON decoding may be when given user input, for instance).

Proposal

This RFC instead proposes adding a new option flag value for json_decode() and json_encode(), JSON_THROW_ON_ERROR. When passed this flag, the error behaviour of these functions is changed. The global error state is left untouched, and if an error occurs that would otherwise set it, these functions instead throw a JsonException with the message and code set to whatever json_last_error() and json_last_error_msg() would otherwise be respectively. JSON_PARTIAL_OUTPUT_ON_ERROR would override and disable JSON_THROW_ON_ERROR, so that generic wrapper functions that always pass the JSON_PARTIAL_OUTPUT_ON_ERROR flag continue to support JSON_PARTIAL_OUTPUT_ON_ERROR.

JsonException would be a new class that subclasses Exception.

Note that when given an invalid depth parameter, json_decode() outputs a warning and returns NULL. This behaviour is unaffected by JSON_THROW_ON_ERROR because it does not affect the global error state at present, has no corresponding error code to throw an exception with, and is not dependent on user input. Similarly, parameter parsing errors continue to produce warnings (when not in strict mode) because their error behaviour cannot be conditioned on the value of a parameter being parsed. Both can be caught by a user error handler that converts errors to exceptions.

At the present time, there would be no change to the default error behaviour. It would be worthwhile considering whether to eventually slowly deprecate not using JSON_THROW_ON_ERROR and then change the default behaviour, but this RFC does not do this.

Backward Incompatible Changes

There is a small possibility of naming conflicts with existing userland code for JsonException and JSON_THROW_ON_ERROR. Since they are in the root namespace (\) and follow the pattern of existing JSON-related items, it could have been reasonably anticipated by users that such names could conflict. It would also be trivial to rename such items. Therefore, it is reasonable not to be concerned about this potential incompatibility.

Proposed PHP Version(s)

The next possible version, most likely 7.3, though as this is a small self-contained feature, it is theoretically possible it could be introduced to 7.2.x.

RFC Impact

To Existing Extensions

The JSON extension is what this RFC concerns.

To Opcache

It shouldn't have any impact on OPcache. In any case, my patch doesn't seem to have caused any problems with OPcache.

Open Issues

None.

Unaffected PHP Functionality

JSON's default error behaviour.

Future Scope

As mentioned earlier, it may be desirable to deprecate the default behaviour eventually.

Vote

This is not a language change, merely a small addition to the JSON extension, so it only technically requires a 50%+1 majority. However, it's a 2/3 vote. It is a simple Yes/No vote on whether to accept this RFC and merge the patch.

Voting started 2017-09-25 and ended 2017-10-05.

Accept and merge JSON_THROW_ON_ERROR for PHP 7.3?
Real name Yes No
ab (ab)  
ajf (ajf)  
ashnazg (ashnazg)  
bukka (bukka)  
bwoebi (bwoebi)  
cmb (cmb)  
colinodell (colinodell)  
davey (davey)  
emir (emir)  
galvao (galvao)  
hywan (hywan)  
jhdxr (jhdxr)  
kelunik (kelunik)  
marcio (marcio)  
mattwil (mattwil)  
mbeccati (mbeccati)  
narf (narf)  
peehaa (peehaa)  
pollita (pollita)  
salathe (salathe)  
tpunt (tpunt)  
trowski (trowski)  
zeev (zeev)  
Final result: 23 0
This poll has been closed.

Patches and Tests

The patch, including tests, can be found here: https://github.com/php/php-src/pull/2662

Implementation

Merged in PHP 7.3: https://github.com/php/php-src/commit/e823770515bd0530bd3c09ea273c720b4df33734

After the project is implemented, this section should contain

  1. a link to the PHP manual entry for the feature

References

This patch and RFC were prompted by two discussions on the php internals mailing list started by Craig Duncan concerning JSON error handling:

Rejected Features

Keep this updated with features that were discussed on the mail lists.

Changelog

  • v1.0.2 - change behaviour to leave global error flag untouched, rather than clearing it; note depth, param-parsing errors
  • v1.0.1 - cover details of the patch not previously mentioned
  • v1.0 - initial version
rfc/json_throw_on_error.txt · Last modified: 2017/10/22 18:44 by ajf