rfc:deprecate-json_encode-nonserializable

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
rfc:deprecate-json_encode-nonserializable [2024/09/03 11:20] pilifrfc:deprecate-json_encode-nonserializable [2024/09/05 10:00] (current) – more detailed analysis of affected classes. Info about workaround for classes with public fields. pilif
Line 1: Line 1:
 ====== PHP RFC: Deprecate json_encode() on classes marked as non-serializable ====== ====== PHP RFC: Deprecate json_encode() on classes marked as non-serializable ======
-  * Version: 0.9 +  * Version: 0.11 
-  * Date: 2024-09-03+  * Date: 2024-09-05
   * Author: Philip Hofstetter, phofstetter@sensational.ch   * Author: Philip Hofstetter, phofstetter@sensational.ch
   * Status: Under Discussion   * Status: Under Discussion
Line 15: Line 15:
 ===== Proposal ===== ===== Proposal =====
  
-This RFC proposes to mark calling <php>json_encode()</php> on instances of classes marked with ''%%ZEND_ACC_NOT_SERIALIZABLE%%'' as deprecated with the longer-term option of throwing an error in the next major version of PHP wich will follow the one this RFC is implemented in.+This RFC proposes to mark calling <php>json_encode()</php> on most instances of classes marked with ''%%ZEND_ACC_NOT_SERIALIZABLE%%'' as deprecated with the longer-term option of throwing an error in the next major version of PHP wich will follow the one this RFC is implemented in.
  
 The flag ''%%ZEND_ACC_NOT_SERIALIZABLE%%'' was intended to mark classes as non-serializable because they either represent a temporary local resource (like a file or database handle) which could not possibly be unserialized later on or because serializing them could have large side-effects (in case of <php>Generator</php> and <php>Iterator</php>). The flag ''%%ZEND_ACC_NOT_SERIALIZABLE%%'' was intended to mark classes as non-serializable because they either represent a temporary local resource (like a file or database handle) which could not possibly be unserialized later on or because serializing them could have large side-effects (in case of <php>Generator</php> and <php>Iterator</php>).
Line 23: Line 23:
 For temporary resources (file handles, etc.) this is potentially an acceptable behavior, albeit a bit inconsistent to how serialization is handled, but for <php>Generator</php>, doing this silently is very inconvenient for a developer in the process of converting a code-base from pre-built arrays to generators for either performance or memory consumption reasons. For temporary resources (file handles, etc.) this is potentially an acceptable behavior, albeit a bit inconsistent to how serialization is handled, but for <php>Generator</php>, doing this silently is very inconvenient for a developer in the process of converting a code-base from pre-built arrays to generators for either performance or memory consumption reasons.
  
-This can be done mostly transparently to the rest of the code-base, but will require special handling for a potential <php>json_encode()</php> which will currently silently do the wrong thing and not just skip iterating the generator but will also silently change the shape of the output, potentially breaking API contracts without any notification to the user.+This can be done mostly transparently to the rest of the code-base, but will require special handling for a potential <php>json_encode()</php> which will currently silently does the wrong thing and not just skip iterating the generator but will also silently change the shape of the output, potentially breaking API contracts without any notification to the user.
  
 +One exception to the rule is anonymous classes which are all marked as ''%%ZEND_ACC_NOT_SERIALIZABLE%%'' because then unserializing, their definition will not be present, so they cannot possibly be unserialized again.
 +
 +In case of <php>json_encode()</php> though, where no generic unserialize operation is defined anyways, there's no reason for deprecating or forbidding to <php>json_encode()</php> anonymous classes, unless their parent class is marked as ''%%ZEND_ACC_NOT_SERIALIZABLE%%'' where the above reasoning applies again.
 +
 +Thus, this RFC proposes to continue to permit <php>json_encode()</php> on anonymous classes unless they extend a class marked as ''%%ZEND_ACC_NOT_SERIALIZABLE%%''.
  
 ==== Other options considered ==== ==== Other options considered ====
Line 35: Line 40:
   - Have <php>json_encode()</php> consume the generator and recurse as if it was encoding an <php>array</php>. While this would probably be the most ergonomic solution for the refactoring case outlined above, given the unforeseeable side-effects generator consumption can have, including endless loops, this is a dangerous operations and whas thus discarded as an option.   - Have <php>json_encode()</php> consume the generator and recurse as if it was encoding an <php>array</php>. While this would probably be the most ergonomic solution for the refactoring case outlined above, given the unforeseeable side-effects generator consumption can have, including endless loops, this is a dangerous operations and whas thus discarded as an option.
   - Have <php>json_encode()</php> encode generators as ''%%[]%%'': This would help the refactoring case by upholding possible API contracts and would more cleanly match the shape of a generator (which is a list after all), but it would also be lying to the calling code because the generator likely won't be empty.   - Have <php>json_encode()</php> encode generators as ''%%[]%%'': This would help the refactoring case by upholding possible API contracts and would more cleanly match the shape of a generator (which is a list after all), but it would also be lying to the calling code because the generator likely won't be empty.
 +
 +==== Impacted internal classes ====
 +
 +At the time of writing this RFC, the following list of classes (and their subclasses) are affected by this RFC  and calling <php>json_encode()</php> on them will throw a deprecation warning in the future.
 +
 +Some of those are containers of a sort, where this encoding is especially misleading (aside of <php>Generator</php> which was the motivator for this RFC, <php>WeakMap</php> stands out specifically).
 +
 +Most of the non-serializable classes have no public properties and thus encode as ''%%{}%%''.
 +
 +Those which currently do have public properties are still mostly meant for internal usage and thus not ideal candidates to <php>json_encode()</php> them. However, if code wants to explicitly turn any such instances into JSON in light of the deprecation currently proposed, casting such instances into <php>array</php> before JSON encoding them is a valid workaround.
 +
 +<code php>
 +$a = new SimpleXmlElement('<a><b>3</b><c>foo</c></a>');
 +echo json_encode($a); // {"b":"3","c":"foo"}, with deprecation warning
 +echo json_enode((array) $a); // {"b":"3","c":"foo"}, no deprecation warning
 +</code>
 +
 +=== Classes with public fields appearing in json_encode() output ===
 +  * CURLFile (has three public properties)
 +  * PDORow (has one public property, queryString)
 +  * PDOStatement (has one public property, queryString)
 +  * SimpleXMLElement (might be useful)
 +  * ReflectionAttribute
 +  * ReflectionClass
 +  * ReflectionClassConstant
 +  * ReflectionConstant
 +  * ReflectionExtension
 +  * ReflectionFiber
 +  * ReflectionFunctionAbstract
 +  * ReflectionGenerator
 +  * ReflectionParameter
 +  * ReflectionProperty
 +  * ReflectionReference
 +  * ReflectionType
 +  * ReflectionZendExtension
 +
 +
 +=== Backed by temporary resources ===
 +  * AddressInfo
 +  * Collator
 +  * CurlHandle
 +  * CurlMultiHandle
 +  * CurlShareHandle
 +  * DOMXPath
 +  * Dba\Connection
 +  * DeflateContext
 +  * Dom\Implementation
 +  * Dom\NamespaceInfo
 +  * Dom\TokenList
 +  * Dom\XPath
 +  * Dom\XMLDocument
 +  * EnchantBroker
 +  * EnchantDictionary
 +  * FFI
 +  * FFI\CData
 +  * FFI\CType
 +  * FTP\Connection
 +  * GdFont
 +  * GdImage
 +  * InflateContext
 +  * IntlBreakIterator
 +  * IntlCalendar
 +  * IntlCodePointBreakIterator
 +  * IntlDateFormatter
 +  * IntlDatePatternGenerator
 +  * IntlIterator
 +  * IntlPartsIterator
 +  * IntlRuleBasedBreakIterator
 +  * IntlTimeZone
 +  * LDAP\Connection
 +  * LDAP\Result
 +  * LDAP\ResultEntry
 +  * MessageFormatter
 +  * NumberFormatter
 +  * Odbc\Connection
 +  * Odbc\Result
 +  * OpenSSLAsymmetricKey
 +  * OpenSSLCertificate
 +  * OpenSSLCertificateSigningRequest
 +  * PDO
 +  * Pdo\Dblib
 +  * Pdo\Firebird
 +  * Pdo\Mysql
 +  * Pdo\Odbc
 +  * Pdo\Pgsql
 +  * Pdo\Sqlite
 +  * PgSql\Connection
 +  * PgSql\Lob
 +  * PgSql\Result
 +  * Random\Engine\Secure
 +  * ResourceBundle
 +  * SQLite3
 +  * SQLite3Result
 +  * SQLite3Stmt
 +  * Shmop
 +  * Soap\Sdl
 +  * Soap\Url
 +  * Socket
 +  * SplFileInfo
 +  * Spoofchecker
 +  * SysvMessageQueue
 +  * SysvSemaphore
 +  * SysvSharedMemory
 +  * Transliterator
 +  * UConverter
 +  * XMLParser
 +  * finfo
 +  * variant
 +
 +=== Other ===
 +  * Closure
 +  * Fiber
 +  * Generator
 +  * InternalIterator
 +  * SensitiveParameterValue
 +  * WeakMap
 +  * WeakReference
 +
  
 ===== Backward Incompatible Changes ===== ===== Backward Incompatible Changes =====
Line 72: Line 195:
  
 ===== Patches and Tests ===== ===== Patches and Tests =====
 +https://github.com/php/php-src/pull/15724
  
 ===== Implementation ===== ===== Implementation =====
rfc/deprecate-json_encode-nonserializable.1725362420.txt.gz · Last modified: 2024/09/03 11:20 by pilif