rfc:null_coercion_consistency

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
Next revisionBoth sides next revision
rfc:null_coercion_consistency [2022/04/09 08:49] – Add user-defined function example craigfrancisrfc:null_coercion_consistency [2022/04/11 16:32] – Update note about user-defined functions specifying parameter types (thanks George Peter Banyard) craigfrancis
Line 1: Line 1:
 ====== PHP RFC: NULL Coercion Consistency ====== ====== PHP RFC: NULL Coercion Consistency ======
  
-  * Version: 0.1+  * Version: 0.2
   * Voting Start: ?   * Voting Start: ?
   * Voting End: ?   * Voting End: ?
Line 20: Line 20:
 Roughly **85%** scripts do not use //strict_types=1// (calculation below). Roughly **85%** scripts do not use //strict_types=1// (calculation below).
  
-Roughly **33%** of developers use static analysis (optimistic, details below).+Roughly **33%** of developers use static analysis (realistically it's less than this, details below).
  
 There was a [[https://externals.io/message/112327|short discussion]] about the original RFC, but with the exception of Craig Duncan, there was no consideration for the problems this creates with existing code (or the inconsistency of NULL coercion compared to string/int/float/bool coercion). There was a [[https://externals.io/message/112327|short discussion]] about the original RFC, but with the exception of Craig Duncan, there was no consideration for the problems this creates with existing code (or the inconsistency of NULL coercion compared to string/int/float/bool coercion).
  
-===== Documentation =====+===== Problem ===== 
 + 
 +==== Documentation ====
  
 According to the documentation, when **not** using //strict_types=1//, "PHP will coerce values of the wrong type into the expected scalar type declaration if possible" ([[https://www.php.net/manual/en/language.types.declarations.php#language.types.declarations.strict|ref]]). According to the documentation, when **not** using //strict_types=1//, "PHP will coerce values of the wrong type into the expected scalar type declaration if possible" ([[https://www.php.net/manual/en/language.types.declarations.php#language.types.declarations.strict|ref]]).
  
-Where coercion from NULL is well defined:+Coercion from NULL is well defined:
  
   - [[https://www.php.net/manual/en/language.types.string.php|To String]]: "null is always converted to an empty string."   - [[https://www.php.net/manual/en/language.types.string.php|To String]]: "null is always converted to an empty string."
Line 68: Line 70:
 </code> </code>
  
-With user-defined functions, while this does not cause a backwards compatibility issue (details below), it still highlights the coercion inconstancy, and that some type checking is happening in an environment that does not expect type checking:+With user-defined functions, while this does not cause a backwards compatibility issue (details below), it still highlights the coercion inconsistency, and that some type checking is happening in an environment that does not expect type checking:
  
 <code php> <code php>
 function user_function(string $s, int $i, float $f, bool $b) { function user_function(string $s, int $i, float $f, bool $b) {
   var_dump($s, $i, $f, $b);   var_dump($s, $i, $f, $b);
-  echo "\n"; 
 } }
  
Line 98: Line 99:
 NULL can usually be coerced, but... NULL can usually be coerced, but...
  
-  - PHP 7.0 introduced the ability for user-defined functions to specify parameter types via the [[https://wiki.php.net/rfc/scalar_type_hints_v5#behaviour_of_weak_type_checks|Scalar Type Declarations RFC]], where the focus was on //strict_types=1//. But the implementation caused Type Error when coercing NULL for everyone (even when not using //strict_types=1//)this seems more of an over-sight, with developers not using //strict_types=1// being unlikely to notice (as they won't specify types in their user defined functions).+  - PHP 7.0 introduced the ability for user-defined functions to specify parameter types via the [[https://wiki.php.net/rfc/scalar_type_hints_v5#behaviour_of_weak_type_checks|Scalar Type Declarations RFC]], where the focus was on //strict_types=1//. But the implementation also caused Type Errors when not using //strict_types=1//, which seems more of an over-sight (breaking null coercionand introducing a type check, for an environment that does not expect this).
   - PHP 8.1 continued this inconsistency with internal functions.   - PHP 8.1 continued this inconsistency with internal functions.
  
-===== Examples =====+==== Examples ====
  
 Common sources of NULL: Common sources of NULL:
Line 122: Line 123:
 </code> </code>
  
-Examples where NULL has been fine for scripts not using //strict_types=1//:+Examples where NULL has previously been fine for scripts not using //strict_types=1//:
  
 <code php> <code php>
Line 156: Line 157:
 There are approximately [[https://github.com/craigfrancis/php-allow-null-rfc/blob/main/functions-change.md|335 parameters affected by this deprecation]]. There are approximately [[https://github.com/craigfrancis/php-allow-null-rfc/blob/main/functions-change.md|335 parameters affected by this deprecation]].
  
-As an aside, there are also roughly [[https://github.com/craigfrancis/php-allow-null-rfc/blob/main/functions-maybe.md|104 questionable]] and [[https://github.com/craigfrancis/php-allow-null-rfc/blob/main/functions-other.md|558 problematic]] parameters which probably shouldn't accept NULL **or** an Empty String. For example, //$separator// in //explode()// already has a "cannot be empty" Fatal Error. For these parameters, a different RFC could consider updating them to reject both NULL and Empty Strings, e.g. $needle in strpos(), and $json in json_decode().+As an aside, there are also roughly [[https://github.com/craigfrancis/php-allow-null-rfc/blob/main/functions-maybe.md|104 questionable]] and [[https://github.com/craigfrancis/php-allow-null-rfc/blob/main/functions-other.md|558 problematic]] parameters which probably shouldn't accept NULL **or** an Empty String. For these parameters, a different RFC could consider updating them to reject both NULL and Empty Strings, e.g. //$needle// in //strpos()//, and //$characters// in //trim()//; in the same way that //$separator// in //explode()// already has a "cannot be empty" Fatal Error.
  
-===== Finding =====+==== Finding ====
  
 The only realistic way for developers to find when NULL is passed to these internal functions is to use the deprecation notices (not ideal). The only realistic way for developers to find when NULL is passed to these internal functions is to use the deprecation notices (not ideal).
  
-It is possible to use very strict Static Analysis, to follow every variable from source to sink (to check if a variable could be //NULL//), but most developers are not in a position to do this.+It is possible to use very strict Static Analysis, to follow every variable from source to sink (to check if a variable could be //NULL//), but most developers are not in a position to do this (i.e. not using static analysis, or not at a high enough level, or they are using a baseline to ignore).
  
-In the last JetBrains developer survey, with 67% of the responses regularly using Laravel, **only 33% used Static Analysis** ([[https://www.jetbrains.com/lp/devecosystem-2021/php/#PHP_do-you-use-static-analysis|source]]); where it's fair to say many still would not identify these possible NULL values.+In the last JetBrains developer survey, where 67% regularly used Laravel, **only 33% used Static Analysis** ([[https://www.jetbrains.com/lp/devecosystem-2021/php/#PHP_do-you-use-static-analysis|source]]); where it's fair to say many still would not identify these possible NULL values.
  
 As an example, take this simple script: As an example, take this simple script:
Line 249: Line 250:
 Note: Juliette (@jrfnl) has confirmed that getting PHPCompatibility to solve this problem will be "pretty darn hard to do" because it's "not reliably sniffable" ([[https://twitter.com/jrf_nl/status/1497937320766496772|source]]). Note: Juliette (@jrfnl) has confirmed that getting PHPCompatibility to solve this problem will be "pretty darn hard to do" because it's "not reliably sniffable" ([[https://twitter.com/jrf_nl/status/1497937320766496772|source]]).
  
-===== Temporary Solutions =====+==== Temporary Solutions ====
  
 You can disable //E_DEPRECATED// (as recommended by projects like WordPress). You can disable //E_DEPRECATED// (as recommended by projects like WordPress).
Line 268: Line 269:
 And some developers are simply [[https://externals.io/message/116519#116559|patching php-src]] (risky). And some developers are simply [[https://externals.io/message/116519#116559|patching php-src]] (risky).
  
-===== Updating =====+==== Updating ====
  
 While making each change is fairly easy - they are difficult to find, there are many of them (time consuming), and the updates used are often pointless, e.g. While making each change is fairly easy - they are difficult to find, there are many of them (time consuming), and the updates used are often pointless, e.g.
Line 291: Line 292:
 Must keep the spirit of the original RFC, and keep user-defined and internal functions consistent. Must keep the spirit of the original RFC, and keep user-defined and internal functions consistent.
  
-But continue to support NULL coercion into an empty string, the integer/float 0or the boolean false.+Revert the deprecation of NULL coercion when **not** using //strict_types=1//, to work as documented, be consistent with scalar types, and (most importantly) avoid the upgrade problems (i.e. Fatal Errors for PHP 9.0).
  
-The current intention of causing Fatal Errors in 9.0 will make upgrading very difficult.+For consistency, change user-defined functions to allow NULL to be coerced when **not** using //strict_types=1//.
  
 ===== Backward Incompatible Changes ===== ===== Backward Incompatible Changes =====
Line 321: Line 322:
 "terrible idea" - I'm still waiting to hear details. "terrible idea" - I'm still waiting to hear details.
  
-"it's a bit late" - We only have a deprecation at the moment (which can be ignored), it will be "too late" when PHP 9.0 uses Fatal Errors.+"it's a bit late" - We only have a deprecation at the moment (which can and is being ignored), it will be "too late" when PHP 9.0 uses Fatal Errors.
  
 ===== Future Scope ===== ===== Future Scope =====
  
-Some function parameters could be updated to rase a Fatal Error when //NULL// **or** //Empty String// is provided; e.g. //$method// in //method_exists()//, or //$characters// in //trim()//.+Some function parameters could be updated to rase a Fatal Error when //NULL// **or** //Empty String// is provided; e.g. 
 + 
 +  - //$needle// in [[https://php.net/strpos|strpos()]] 
 +  - //$characters// in [[https://php.net/trim|trim()]] 
 +  - //$method// in [[https://php.net/method_exists|method_exists()]] 
 +  - //$json// in [[https://php.net/json_decode|json_decode()]]
  
 ===== Voting ===== ===== Voting =====
rfc/null_coercion_consistency.txt · Last modified: 2023/10/18 11:57 by craigfrancis