rfc:allow_null

Differences

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

Link to this comparison view

Next revision
Previous revision
Next revisionBoth sides next revision
rfc:allow_null [2021/12/23 22:07] – created craigfrancisrfc:allow_null [2022/02/08 15:31] craigfrancis
Line 1: Line 1:
 ====== PHP RFC: Allow NULL ====== ====== PHP RFC: Allow NULL ======
  
-  * Version: 1.0+  * Version: 1.2
   * Voting Start: ?   * Voting Start: ?
   * Voting End: ?   * Voting End: ?
   * RFC Started: 2021-12-23   * RFC Started: 2021-12-23
-  * RFC Updated: 2021-12-23+  * RFC Updated: 2021-12-31
   * Author: Craig Francis, craig#at#craigfrancis.co.uk   * Author: Craig Francis, craig#at#craigfrancis.co.uk
-  * Status: ?+  * Status: Draft
   * First Published at: https://wiki.php.net/rfc/allow_null   * First Published at: https://wiki.php.net/rfc/allow_null
   * GitHub Repo: https://github.com/craigfrancis/php-allow-null-rfc   * GitHub Repo: https://github.com/craigfrancis/php-allow-null-rfc
Line 14: Line 14:
 ===== Introduction ===== ===== Introduction =====
  
-PHP 8.1 introduced "Passing null to parameter X of type string is deprecated" with internal functions ([[https://externals.io/message/112327|discussion]]), which is making it difficult for developers to upgrade, especially for those who do not use //strict_types//.+PHP 8.1 introduced "Deprecate passing null to non-nullable arguments of internal functions([[https://externals.io/message/112327|short discussion]]), which is making it difficult (time consuming) for developers to upgrade.
  
-Often //NULL// is used for undefined //GET/////POST/////COOKIE// variables:+In PHP //NULL// is often used to represent something; e.g. when a //GET/////POST/////COOKIE// variable is undefined:
  
 <code php> <code php>
Line 27: Line 27:
 </code> </code>
  
-And //NULL// can be returned from many functions, e.g.+And //NULL// can be returned from functions, e.g.
  
   * //array_pop()//   * //array_pop()//
-  * //error_get_last()//+  * //filter_input()//
   * //mysqli_fetch_row()//   * //mysqli_fetch_row()//
 +  * //error_get_last()//
   * //json_decode()//   * //json_decode()//
-  * //filter_input()// 
  
-Currently this change only affects those using PHP 8.1 with //E_DEPRECATED//, but it implies everyone will need to modify their code in the future.+This makes it common for //NULL// to be passed to many internal functions, e.g.
  
-And those modifications are not easy (both in finding themand quantity).+<code php> 
 +trim($name); 
 +strtoupper($name); 
 +strlen($name); 
 +urlencode($name); 
 +htmlspecialchars($name); 
 +hash('sha256', $name); 
 +preg_match('/^[a-z]/', $name); 
 +setcookie('name', $name); 
 +socket_write($socket, $name); 
 +xmlwriter_text($writer$name)
 +</code>
  
-Developers will need to either use these deprecation warnings, or use strict static analysis (one that can determine when a variable can be //NULL//; e.g. Psalm at [[https://psalm.dev/docs/running_psalm/error_levels/|level 3]], with no baseline).+Where //NULL// has the advantage of falling back to being treated like an empty string.
  
-This affects every variable that could be set to //NULL//, when they are passed to any internal function that has deprecated the NULL valueAnd every timedevelopers will need to change their code, typically with the seemingly pointless task of converting those //NULL// values to an empty string.+Developers also use //NULL// to skip certain parameters, e.g. //$additional_headers// in //mail()//
 + 
 +Currently the deprecation notices only affect those using PHP 8.1 with //E_DEPRECATED//but it implies everyone will need to modify their code to avoid **Fatal Errors** in PHP 9.0. 
 + 
 +Developers using //strict_types=1// may find some value in thisbut it's excessive for everyone else. 
 + 
 +And while individual changes are easy, there are many of them, are difficult to find, and often pointless, e.g. 
 + 
 +  * urlencode(strval($name)); 
 +  * urlencode((string) $name); 
 +  * urlencode($name ?? ''); 
 + 
 +To find these issues, developers need to either - use these deprecation notices, or use very strict Static Analysis (one that can determine when a variable can be //NULL//; e.g. Psalm at [[https://psalm.dev/docs/running_psalm/error_levels/|level 3]], with no baseline).
  
 ===== Proposal ===== ===== Proposal =====
  
-To make upgrading easierwe could either:+Update **some** internal function parameters to accept (or be tolerant to) //NULL//. 
 + 
 +This needs to be done before the eventual end of the deprecation periodand //TypeError// exceptions are thrown - which would create an unnecessary burden for developers to upgrade. 
 + 
 +While this is in Draft, the [[https://github.com/craigfrancis/php-allow-null-rfc/blob/main/functions-change.md|list of functions are hosted on GitHub]]. 
 + 
 +Only the parameters in **bold** would be changed. 
 + 
 +[[https://github.com/craigfrancis/php-allow-null-rfc/issues|Suggestions]] and [[https://github.com/craigfrancis/php-allow-null-rfc/pulls|pull requests]] welcome. 
 + 
 +There is also a [[https://github.com/craigfrancis/php-allow-null-rfc/blob/main/functions-maybe.md|Maybe List]], where the more questionable arguments end with a "!". For example, //strrpos()// accepting an empty string for //$needle// is wired in itself, and //sodium_crypto_box_open()// should never receive a blank //$ciphertext//. And there is an [[https://github.com/craigfrancis/php-allow-null-rfc/blob/main/functions-other.md|Other List]], which can be ignored. 
 + 
 +===== Decision Process ===== 
 + 
 +Does //NULL// for this parameter justify a Fatal Error? e.g.
  
-  - Allow //NULL// when //strict_types// is not being used; +  - //preg_match()// should **deprecate** //NULL// for //$pattern// ("empty regular expression" warning). 
-  - Update some of the internal functions to accept //NULL//.+  - //preg_match()// should **accept** //NULL// for //$subject// (e.g. checking user input). 
 +  - //hash_file()// should **deprecate** //NULL// for the //$filename//
 +  - //hash()// should **accept** //NULL// for //$data//
 +  - //substr_count()// should **deprecate** //NULL// for //$needle// ("$needle cannot be empty" error). 
 +  - //mb_convert_encoding()// should **deprecate** //NULL// for //$to_encoding// (requires a valid encoding).
  
 ===== Backward Incompatible Changes ===== ===== Backward Incompatible Changes =====
  
-N/A+None
  
 ===== Proposed PHP Version(s) ===== ===== Proposed PHP Version(s) =====
  
-PHP 8.1+PHP 8.2
  
 ===== RFC Impact ===== ===== RFC Impact =====
Line 74: Line 115:
 ===== Open Issues ===== ===== Open Issues =====
  
-TODO+Is the [[https://github.com/craigfrancis/php-allow-null-rfc/blob/main/functions-change.md|list of functions]] complete?
  
 ===== Future Scope ===== ===== Future Scope =====
  
-TODO+Some function parameters could be updated to complain when an Empty String or //NULL// is provided; e.g. //$method// in //method_exists()//, or //$characters// in //trim()//.
  
 ===== Voting ===== ===== Voting =====
Line 86: Line 127:
 TODO TODO
  
-===== Patches and Tests =====+===== Tests =====
  
-TODO+To get and **Test** the list of functions, I wrote a script to //get_defined_functions()//, then used //ReflectionFunction()// to identify parameters that accepted the 'string' type, and not //->allowsNull()//. This resulted in the [[https://github.com/craigfrancis/php-allow-null-rfc/blob/main/functions-change.md|list of functions to change]], where I manually removed the [[https://github.com/craigfrancis/php-allow-null-rfc/blob/main/functions-other.md|functions that shouldn't be changed]], and updated the script to test every argument (to see that it complained with //NULL//, and the output remained the same) - [[https://github.com/craigfrancis/php-allow-null-rfc/blob/main/functions.php|Source]].
  
 ===== Implementation ===== ===== Implementation =====
  
-TODO+https://github.com/craigfrancis/php-src/compare/master...allow-null
  
-===== Rejected Features =====+This patch defines //Z_PARAM_STR_ALLOW_NULL//.
  
-TODO+It works a bit like //Z_PARAM_STR_OR_NULL//, but it will return an empty string instead of //NULL//.
  
 +It's a fairly easy drop in replacement for //Z_PARAM_STR//, e.g. [[https://github.com/php/php-src/blob/7b90ebeb3f954123915f6d62fb7b2cd3fdf3c6ec/ext/standard/html.c#L1324|htmlspecialchars()]].
  
 +===== Rejected Features =====
  
----+TODO
  
-====== Notes ======+===== Notes =====
  
-Interesting to note that this goes against the example quote from [[http://news.php.net/php.internals/71525|Rasmus]]:+Interesting the example quote from [[http://news.php.net/php.internals/71525|Rasmus]] is:
  
 > PHP is and should remain: > PHP is and should remain:
Line 110: Line 153:
 > 2) a loosely typed language > 2) a loosely typed language
 > 3) a language which caters to the skill-levels and platforms of a wide range of users > 3) a language which caters to the skill-levels and platforms of a wide range of users
- 
- 
- 
-grep -h -r -E '^\s*(ZEND_FUNCTION|PHP_FUNCTION|static void)|Z_PARAM_STR\(' ./php-src/ 
- 
-Search 
-  ^(\s*(ZEND_FUNCTION|PHP_FUNCTION|static void).*\n)*(ZEND_FUNCTION\(|PHP_FUNCTION\(|static void *)(.+?)(\)|\().* 
-Replace 
-  $4 
  
rfc/allow_null.txt · Last modified: 2022/04/05 17:49 by craigfrancis