package.xml0000664000175000017500000013276514366750307012340 0ustar tysontyson igbinary pecl.php.net igbinary extension Igbinary is a drop in replacement for the standard php serializer. Instead of the time and space consuming textual representation used by PHP's serialize(), igbinary stores php data structures in a compact binary form. Savings are significant when using memcached or similar memory based storages for serialized data. Oleg Grenrus phadej oleg.grenrus@iki.fi yes Pierre Joye pajoye pierre@php.net yes Teddy Grenman tricky teddy.pecl@luuseri.com yes Tyson Andre tandre tandre@php.net yes 2023-02-02 3.2.13 1.4.0 stable stable BSD-3-Clause * Speed up unserialization of typed properties by reducing hash table collisions when looking up property reference info. 7.0.0 1.4.0b1 igbinary 2022-11-07 3.2.12 1.4.0 stable stable BSD-3-Clause * Fix symbol error seen in php 8.2.0 loading zend_class_unserialize_deny, due to failing to load a header defining a macro. 2022-11-06 3.2.11 1.4.0 stable stable BSD-3-Clause * Fix a bug that could prevent objects/arrays with reference cycles from being properly garbage collected. * Fix bugs in unserializing PHP references to values found in php 7.4 typed properties (#363) 2022-11-06 3.2.10 1.4.0 stable stable BSD-3-Clause * Add a macro that callers can use to check if igbinary will accept the header for data being unserialized. * Fix bug preventing the unserialization of data containing representations of strings larger than 4GB. 2022-10-17 3.2.9 1.3.1 stable stable BSD-3-Clause * Fix invalid release artifact name in job to build dlls for https://github.com/igbinary/igbinary 2022-10-16 3.2.8 1.3.1 stable stable BSD-3-Clause * Reduce excessive inlining to reduce shared library size. * Miscellaneous optimizations. * Update test expectations to handle changes to var_export output (fully qualified class names) in PHP 8.2. * Throw an Error when igbinary_unserialize would add dynamic properties to class definitions that forbid them in PHP 8.0+ (especially PHP 8.2 `readonly` classes) * Emit a deprecation notice when igbinary_unserialize() adds dynamic properties to a class without `#[AllowDynamicProperties]` in PHP 8.2. Doing that would become an Error in PHP 9.0. * Set up CI job to build dlls on https://github.com/igbinary/igbinary - at the moment, the infrastructure used by the Windows for php team has been broken for months. 2021-08-11 3.2.7 1.3.1 stable stable BSD-3-Clause * Update test expectations for php 8.2.0-dev. Add `#[AllowDynamicProperties]` Attribute to some tests to avoid notices. * In php 8.1+, make igbinary_unserialize check to see if an equivalent interned string already exists when unserializing object property names, array keys, and class names and use that instead of creating a brand new string. (This deliberately doesn't create a new interned string if one doesn't already exist.) (Before this change, igbinary would deduplicate strings when serializing, but would not check if strings were interned by PHP itself when unserializing) * Avoid debug build assertion failure for `HT_ASSERT_RC1` the same way as PHP's unserialize - this is a case where ostensibly there are no other references to the array being unserialized. 2021-08-11 3.2.6 1.3.1 stable stable BSD-3-Clause * Fix igbinary extension version found in reflection. 2021-08-07 3.2.5 1.3.1 stable stable BSD-3-Clause * Fix change in behavior introduced in 3.2.2RC1 when unserializing arrays - the internal array pointer (for `next()`, `key()`, etc) pointed past the end of the array in php 7.0-7.2. 2021-07-24 3.2.4 1.3.1 stable stable BSD-3-Clause * Forbid serializing classes that deny serialization/unserialization (anonymous classes, CURLFile, etc.) even when subclasses implement '__serialize' and '__unserialize' 2021-06-09 3.2.3 1.3.1 stable stable BSD-3-Clause * Fix build for php 8.1 after changes to enum internals. * Update tests to suppress deprecations in php 8.1 and support run-tests.php changes in php 8.1 * Don't emit a notice when unserialize_callback_func causes igbinary_unserialize to throw https://bugs.php.net/bug.php?id=81118 2021-04-18 3.2.2 1.3.0 stable stable BSD-3-Clause * Eliminate impossible/redundant checks. * Add a new type code for serialization and unserialization of PHP strings that are larger than 4GB. * Add additional checks for overflow when serializing extremely large data structures. (e.g. serializing more than 2**32 strings or 2**32 objects/references/arrays) * Support serializing and unserializing php 8.1 enums (can only be unserialized in php 8.1+) 2021-01-11 3.2.2RC1 1.2.5 beta stable BSD-3-Clause * Update php version check to allow igbinary to be statically built in PHP 8.0+ * Fix bug in out of memory error handling in __sleep, slightly speed up serializing with __sleep. * Continue serializing remaining properties if a missing property name is returned from __sleep. * Speed up serializing by optimizing for the case where there is no memory manager override. When there is a memory manager override, only use that for allocating the string to return. (benchmarks/serialize-scalar-int.b.php showed a speedup from 0.22 to 0.18 seconds for repeated serialization of a single scalar, and from 0.186 to 0.180 seconds for benchmarks/serialize-stringarray.b.php for an array of strings) * Speed up unserializing arrays in php 7.2-8.0 by adding optimized code for finding the hash bucket of a string/integer key of an array, or creating a placeholder if it does not already exist. 2020-12-27 3.2.1 1.2.5 stable stable BSD-3-Clause * Fix crash when unserializing if __serialize was defined but __unserialize was undefined in php 8.0+ (due to typo). 2020-12-26 3.2.0 1.2.5 stable stable BSD-3-Clause * Use PHP's shared empty array instance when unserializing empty arrays in php 7.3+. (helps slightly with memory usage when repeatedly unserializing, when removing elements from arrays before unserializing them, or when serializing values including an empty array that was unserialized) * Emit a deprecation notice when serializing resources. PHP itself is converting many resources to objects that throw an Error on serialization attempts. Continue to represent resources as null in the serialized data. * Fix memory management bug when unserializing invalid data (duplicate properties in objects (e.g. from `__sleep`) or duplicate fields in arrays (impossible for valid data)). * Speed up calls to `__serialize`/`__unserialize` in php 8.0+. * Fix error messages for unserialize_callback_func: make messages properly refer to the autoload function. * Optimize unserializing alternative names for private/protected properties that were previously public. 2020-10-08 3.1.6 1.2.4 stable stable BSD-3-Clause * Fix build failure with older C standard (e.g. building on CentOS 6). * Otherwise, identical to 3.1.6RC1. 2020-10-07 3.1.6RC1 1.2.4 beta stable BSD-3-Clause * Fix igbinary_serialize incorrectly deduplicating arrays/objects/references when they were garbage collected/freed during serialization. 2020-09-02 3.1.5 1.2.3 stable stable BSD-3-Clause * Update unit test expectation to match behavior in php 8 due to changes in php's handling of cyclic references in arrays. * Support API changes in php 8.0.0beta3. 2020-08-05 3.1.4 1.2.3 stable stable BSD-3-Clause * Fix unserialization of PHP references to internal/user-defined classes using PHP 7.4's `__unserialize` (e.g. `ArrayObject`) 2020-08-04 3.1.3 1.2.3 stable stable BSD-3-Clause * Properly serialize reference groups of size 1 (these can be created by array_walk_recursive and other functions). Note that this does not fix the general case where values not being serialized are in the same reference group as a value being serialized. * PHP 8.0 compatibility fixes. 2020-01-21 3.1.2 1.2.3 stable stable BSD-3-Clause * Speed up object, array, reference, and string serialization. * Speed up unserializing integers between 0 and 65535 (as values and array keys). * Speed up unserializing objects with declared properties. 2020-01-16 3.1.1 1.2.3 stable stable BSD-3-Clause * Fix bug causing incorrect serialization for 1 in 2**32 strings on 64-bit php installations when string hashes collide. (https://github.com/igbinary/igbinary/issues/260) 2021-01-11 3.1.1a1 1.2.2 beta beta BSD-3-Clause * Throw when an uninitialized php 7.4 typed property is included in the result of __sleep(), instead of emitting a notice and attempting to represent the unset/uninitialized value as null (#258). See https://bugs.php.net/bug.php?id=79002 Uninitialized properties without types (from __sleep) continue to cause igbinary to emit notices and are represented as null. 2019-12-27 3.1.0 1.2.2 beta beta BSD-3-Clause * Same as 3.1.0b4. 2019-12-20 3.1.0b4 1.2.2 beta beta BSD-3-Clause * Don't call __destruct for objects where deferred __unserialize calls were not started (e.g. due to Serializable::unserialize throwing). 2019-12-10 3.1.0b3 1.2.2 beta beta BSD-3-Clause * Skip over object properties that are uninitialized or unset when serializing, instead of serializing them as null. This is done to avoid Errors when unserializing their values for php 7.4 typed properties. 2019-12-09 3.1.0b2 1.2.1 beta beta BSD-3-Clause * Fix crashes related to unserializing instances of classes with php 7.4 typed properties. 2019-12-08 3.1.0b1 1.2.1 beta beta BSD-3-Clause * Support php 7.4's __serialize/__unserialize the same way serialize()/unserialize() does. This deliberately only supports __serialize/__unserialize in php 7.4, to making switching to/from serialize()/unserialize() as straightforward as possible. 2019-03-20 3.0.1 1.2.0 stable stable BSD-3-Clause * Fix version check when statically building igbinary inside of the php-src folder. 2019-02-17 3.0.0 1.2.0 stable stable BSD-3-Clause * Identical to 3.0.0a2 2019-02-13 3.0.0a2 1.2.0 alpha stable BSD-3-Clause * Don't use empty string for serializing empty $_SESSION array, it breaks some save handlers. (Issue #231) Continue treating the empty string as the empty $_SESSION array when unserializing. 2019-02-09 3.0.0a1 1.2.0 alpha stable BSD-3-Clause * Drop support for PHP 5. * Drop support for APC (APC was only available for PHP5 - It is the predecessor of APCu) * Emit a warning and return null if igbinary_unserialize() is passed more data to unserialize than expected. * Fix compilation against PHP 7.4-dev. Igbinary does NOT yet properly serialize/unserialize all classes with PHP 7.4-dev's typed properties. * The serialization format is exactly the same as igbinary 2.x 2018-10-20 2.0.8 1.1.2 stable stable BSD-3-Clause * Be more aggressive about deduplication when generating serialization of arrays in php 7.0+. * Define HAVE_IGBINARY on Unix/Linux. (previously defined only on Windows) * Update formatting/wording of documentation. 2018-06-27 2.0.7 1.1.1 stable stable BSD-3-Clause * Fix compiler warnings about format strings, for errors that should not occur during normal igbinary usage. 2018-05-12 2.0.6 1.1.1 beta stable BSD-3-Clause * Same as 2.0.6RC1 2018-04-01 2.0.6RC1 1.1.1 stable stable BSD-3-Clause * Same as 2.0.6RC1 * Fix a bug in Windows debug builds. * Emit more specific warnings when __sleep() returns a declared property that was unset. * Fix harmless compiler warnings during builds. * Fix a build error on PHP7.3-dev. 2017-11-04 2.0.5 1.1.1 stable stable BSD-3-Clause * Same as 2.0.5RC1 (no bugs were reported in that release candidate) 2017-10-15 2.0.5RC1 1.1.1 beta stable BSD-3-Clause * Improve performance when unserializing objects/arrays and serializing objects/arrays/strings in php 5/7. * Update unserialization of integer object keys for php 7.2: Make those keys accessible when unserializing. * Properly pick up presence of gcc for default compiler flags (`cc --version` doesn't contain gcc). Add -O2 to default gcc compiler flags. * Use empty string for serializing empty $_SESSION array, similar to "session.serialize_handler=php". Older igbinary releases already unserialize the empty string to the empty array. 2017-04-14 2.0.4 1.1.1 stable stable BSD-3-Clause * Fixes bug #129: Should not call __wakeup() on data which was created by Serializable::unserialize() 2017-03-31 2.0.3 1.1.1 stable stable BSD-3-Clause - Fixes bug #126: Fatal error: "igbinary_serialize_zval: zval has unknown type 0" (IS_UNDEF) Make this a warning instead of a fatal error (and serialize as null instead), since IS_UNDEF is a known type. Later releases will fix the root cause of the warning, and consistently omit array/object/other entries for IS_UNDEF. 2017-02-28 2.0.2 1.1.1 stable stable BSD-3-Clause - Compatible with PHP 5.2 - 7.1 - Fixes crash in Memcached->setMulti (in php 7.0+) when the first level of array elements have references as values. Other extensions using igbinary shouldn't be affected. 2016-11-19 2.0.1 1.1.1 stable stable BSD-3-Clause - Compatible with PHP 5.2 - 7.0 - Fixes bug in session decoder not calling wakeup in php 7.0+ - (Enhancement) Reuses identical strings when unserializing objects and arrays in php 7.0+ 2016-10-30 2.0.0 1.1.1 stable stable BSD-3-Clause - Compatible with PHP 5.2 - 7.0 (Adds PHP 7 support) - Serialization format is unchanged - Performance improvements for serialization and unserialization in PHP 5.2 - 7.0 - (PHP 7) Don't call __destruct if __wakeup threw an exception (or __wakeup wasn't called yet) - Ports integration with other extensions to PHP 7.0 (session serialization, Memcached, Redis, APCu, etc.) - Fixes Windows PECL builds for PHP 5.6+ - Reword warnings for invalid header bytes of serialized data (in igbinary_unserialize). 2014-08-29 1.2.1 1.1.1 stable stable BSD-3-Clause-3-Clause - Compatible with PHP 5.2 - 5.6 2014-08-29 1.2.0 1.1.1 stable stable BSD-3-Clause - PECL bug #22614, igbinary_unserialize(FALSE) must return FALSE - PHP bug #54662, unserializing nested objects cause crash - Other fixes 2011-03-14 1.1.1 1.1.1 stable stable BSD-3-Clause - Initial PECL release 2009-09-02 1.0.2 1.0.2 stable stable PHP like license - Use Z_ADDREF_PP or Z_ADDREF et al. when increasing refcount. igbinary-3.2.13/tests/__serialize_001.phpt0000664000175000017500000000152014366750307017435 0ustar tysontyson--TEST-- __serialize() mechanism (001): Basics --SKIPIF-- --FILE-- $this->prop, 42 => $this->prop2]; } public function __unserialize(array $data) { $this->prop = $data["value"]; $this->prop2 = $data[42]; } } $test = new Test; $test->prop = "foobar"; $test->prop2 = "barfoo"; var_dump(bin2hex($s = igbinary_serialize($test))); var_dump(igbinary_unserialize($s)); ?> --EXPECT-- string(74) "000000021704546573741402110576616c75651106666f6f626172062a1106626172666f6f" object(Test)#2 (2) { ["prop"]=> string(6) "foobar" ["prop2"]=> string(6) "barfoo" } igbinary-3.2.13/tests/__serialize_002.phpt0000664000175000017500000000073614366750307017446 0ustar tysontyson--TEST-- __serialize() mechanism (002): TypeError on invalid return type --SKIPIF-- --FILE-- getMessage(), "\n"; } ?> --EXPECT-- Test::__serialize() must return an array igbinary-3.2.13/tests/__serialize_003.phpt0000664000175000017500000000332014366750307017437 0ustar tysontyson--TEST-- __serialize() mechanism (003): Interoperability of different serialization mechanisms --SKIPIF-- --INI-- ; Note that php 8.1 deprecates using Serializable without __serialize/__unserialize but we are testing Serialize for igbinary. Suppress deprecations. error_reporting=E_ALL & ~E_DEPRECATED --FILE-- "value"]; } public function __unserialize(array $data) { echo "__unserialize() called\n"; var_dump($data); } public function serialize() { echo "serialize() called\n"; return "payload"; } public function unserialize($payload) { echo "unserialize() called\n"; var_dump($payload); } } $test = new Test; var_dump(bin2hex($s = igbinary_serialize($test))); var_dump(igbinary_unserialize($s)); var_dump(igbinary_unserialize(hex2bin('000000021704546573741d077061796c6f6164'))); ?> --EXPECT-- __serialize() called string(48) "00000002170454657374140111036b6579110576616c7565" __unserialize() called array(1) { ["key"]=> string(5) "value" } object(Test)#2 (0) { } unserialize() called string(7) "payload" object(Test)#2 (0) { } igbinary-3.2.13/tests/__serialize_004.phpt0000664000175000017500000000526714366750307017454 0ustar tysontyson--TEST-- __serialize() mechanism (004): Delayed __unserialize() calls --SKIPIF-- --FILE-- data = $data; } public function __wakeup() { echo "__wakeup() called\n"; var_dump($this->data); $this->woken_up = true; } } #[AllowDynamicProperties] class Unserialize { public $data; public function __construct(array $data) { $this->data = $data; } public function __serialize() { return $this->data; } public function __unserialize(array $data) { $this->data = $data; echo "__unserialize() called\n"; var_dump($this->data); $this->unserialized = true; } } $obj = new Wakeup([new Unserialize([new Wakeup([new Unserialize([])])])]); var_dump(bin2hex($s = igbinary_serialize($obj))); var_dump(igbinary_unserialize($s)); ?> --EXPECT-- string(110) "00000002170657616b657570140111046461746114010600170b556e73657269616c697a65140106001a0014010e01140106001a021400" __unserialize() called array(0) { } __wakeup() called array(1) { [0]=> object(Unserialize)#8 (2) { ["data"]=> array(0) { } ["unserialized"]=> bool(true) } } __unserialize() called array(1) { [0]=> object(Wakeup)#7 (2) { ["data"]=> array(1) { [0]=> object(Unserialize)#8 (2) { ["data"]=> array(0) { } ["unserialized"]=> bool(true) } } ["woken_up"]=> bool(true) } } __wakeup() called array(1) { [0]=> object(Unserialize)#6 (2) { ["data"]=> array(1) { [0]=> object(Wakeup)#7 (2) { ["data"]=> array(1) { [0]=> object(Unserialize)#8 (2) { ["data"]=> array(0) { } ["unserialized"]=> bool(true) } } ["woken_up"]=> bool(true) } } ["unserialized"]=> bool(true) } } object(Wakeup)#5 (2) { ["data"]=> array(1) { [0]=> object(Unserialize)#6 (2) { ["data"]=> array(1) { [0]=> object(Wakeup)#7 (2) { ["data"]=> array(1) { [0]=> object(Unserialize)#8 (2) { ["data"]=> array(0) { } ["unserialized"]=> bool(true) } } ["woken_up"]=> bool(true) } } ["unserialized"]=> bool(true) } } ["woken_up"]=> bool(true) } igbinary-3.2.13/tests/__serialize_005.phpt0000664000175000017500000000310614366750307017443 0ustar tysontyson--TEST-- __serialize() mechanism (005): parent::__unserialize() is safe --SKIPIF-- --FILE-- data = $data; } public function __serialize() { return $this->data; } public function __unserialize(array $data) { $this->data = $data; } } class B extends A { public function __construct(array $data, array $data2) { parent::__construct($data); $this->data2 = $data2; } public function __serialize() { return [$this->data2, parent::__serialize()]; } public function __unserialize(array $payload) { [$data2, $data] = $payload; parent::__unserialize($data); $this->data2 = $data2; } } $common = new stdClass; $obj = new B([$common], [$common]); var_dump(bin2hex($s = igbinary_serialize($obj))); var_dump(igbinary_unserialize($s)); ?> --EXPECT-- string(70) "0000000217014214020600140106001708737464436c61737314000601140106002202" object(B)#3 (2) { ["data":"A":private]=> array(1) { [0]=> object(stdClass)#4 (0) { } } ["data2":protected]=> array(1) { [0]=> object(stdClass)#4 (0) { } } } igbinary-3.2.13/tests/__serialize_006.phpt0000664000175000017500000000231414366750307017444 0ustar tysontyson--TEST-- __serialize() mechanism (006): DateTime --SKIPIF-- --FILE-- --EXPECT-- string(164) "0000000217084461746554696d651403110464617465111a323031392d31322d30382031323a33343a30302e303030303030110d74696d657a6f6e655f747970650603110874696d657a6f6e651103555443" object(DateTime)#2 (3) { ["date"]=> string(26) "2019-12-08 12:34:00.000000" ["timezone_type"]=> int(3) ["timezone"]=> string(3) "UTC" } string(184) "0000000217084461746554696d651403110464617465111a323031392d31322d30382031323a33343a30302e303030303030110d74696d657a6f6e655f747970650603110874696d657a6f6e65110d506163696669632f4e61757275" object(DateTime)#1 (3) { ["date"]=> string(26) "2019-12-08 12:34:00.000000" ["timezone_type"]=> int(3) ["timezone"]=> string(13) "Pacific/Nauru" } igbinary-3.2.13/tests/__serialize_007.phpt0000664000175000017500000000275414366750307017455 0ustar tysontyson--TEST-- __serialize() mechanism (007): handle __unserialize throwing --SKIPIF-- --FILE-- prop, $this->prop2]; } public function __unserialize(array $data) { $this->prop = $data[0]; $this->prop2 = $data[1]; throw new RuntimeException($this->prop); } public function __destruct() { // should not be called echo "Called destruct prop=$this->prop\n"; } } $test = new Test; $test->prop = 'XX'; $test->prop2 = [$test]; // 00000002 - igbinary header // 17 04 54657374 - object of class with name "Test" // 14 02 - 2 properties // 06 00 - uint8(0) => // 11 02 5858 - 'XX' // 06 01 - uint8(1) => // 14 01 - array(size=2) // 06 00 - uint8(0) => // 22 00 - igbinary_type_objref8 (pointer to the first referenceable item, i.e. the instance of "Test" var_dump(bin2hex($s = igbinary_serialize($test))); try { var_dump(igbinary_unserialize($s)); } catch (RuntimeException $e) { echo "Caught: {$e->getMessage()}\n"; } $test->prop = 'not from igbinary_unserialize'; ?> --EXPECT-- string(52) "0000000217045465737414020600110258580601140106002200" Caught: XX Called destruct prop=not from igbinary_unserialize igbinary-3.2.13/tests/__serialize_008.phpt0000664000175000017500000000307114366750307017447 0ustar tysontyson--TEST-- __serialize() mechanism (007): handle __destruct of returned data --SKIPIF-- = 90000) { echo "skip requires php < 9.0 when testing that the deprecation has no impact on igbinary functionality\n"; } ?> --FILE-- = 80200) { require_once __DIR__ . '/php82_suppress_dynamic_properties_warning.inc'; } class DestructorThrows { public function __construct(string $val) { $this->val = $val; } public function __destruct() { echo "DestructorThrows called val=$this->val\n"; throw new RuntimeException($this->val); } } class Xyz { public $Xyz; public function __serialize() { return [new DestructorThrows($this->Xyz)]; } public function __unserialize(array $data) { $this->Xyz = $data[0]; } public function __destruct() { // should not be called echo "Called destruct prop=$this->Xyz\n"; } } $test = new Xyz; $test->Xyz = 'Xyz'; try { var_dump(bin2hex($s = igbinary_serialize($test))); } catch (RuntimeException $e) { echo "message={$e->getMessage()}\n"; } unset($test); $test = new Xyz; $test->Xyz = ''; try { var_dump(bin2hex($s = igbinary_serialize($test))); } catch (RuntimeException $e) { echo "message={$e->getMessage()}\n"; } ?> --EXPECT-- DestructorThrows called val=Xyz message=Xyz DestructorThrows called val= Called destruct prop=Xyz message= Called destruct prop= igbinary-3.2.13/tests/__serialize_009.phpt0000664000175000017500000000431514366750307017452 0ustar tysontyson--TEST-- __serialize() mechanism (009): Object/reference ids should be the same whether or not __serialize is used. --SKIPIF-- --FILE-- value = $value; } } class Test { public $prop; public function __construct($value) { $this->prop = $value; } public function __serialize() { return ["value" => $this->prop]; } public function __unserialize(array $data) { $this->prop = $data["value"]; } } $vest = new Vest('first'); $vest2 = new Vest(null); $vest3 = new Vest($vest2); $sv = igbinary_serialize([$vest, $vest2, $vest3, $vest, $vest2]); $test = new Test('first'); $test2 = new Test(null); $test3 = new Test($test2); $s = igbinary_serialize([$test, $test2, $test3, $test, $test2]); // The only difference in the serialization should be the first byte of the only occurrence of the class name. var_dump(bin2hex($sv)); var_dump(bin2hex($s)); var_dump(igbinary_unserialize($s)); var_dump(igbinary_unserialize($sv)); ?> --EXPECT-- string(114) "00000002140506001704566573741401110576616c75651105666972737406011a0014010e010006021a0014010e0122020603220106042202" string(114) "00000002140506001704546573741401110576616c75651105666972737406011a0014010e010006021a0014010e0122020603220106042202" array(5) { [0]=> object(Test)#7 (1) { ["prop"]=> string(5) "first" } [1]=> object(Test)#8 (1) { ["prop"]=> NULL } [2]=> object(Test)#9 (1) { ["prop"]=> object(Test)#8 (1) { ["prop"]=> NULL } } [3]=> object(Test)#7 (1) { ["prop"]=> string(5) "first" } [4]=> object(Test)#8 (1) { ["prop"]=> NULL } } array(5) { [0]=> object(Vest)#8 (1) { ["value"]=> string(5) "first" } [1]=> object(Vest)#7 (1) { ["value"]=> NULL } [2]=> object(Vest)#9 (1) { ["value"]=> object(Vest)#7 (1) { ["value"]=> NULL } } [3]=> object(Vest)#8 (1) { ["value"]=> string(5) "first" } [4]=> object(Vest)#7 (1) { ["value"]=> NULL } } igbinary-3.2.13/tests/__serialize_010.phpt0000664000175000017500000000302614366750307017440 0ustar tysontyson--TEST-- __serialize() mechanism (010): handle references in array returned by __serialize --SKIPIF-- --FILE-- prop, $this->prop2]; } public function __unserialize(array $data) { $this->prop = $data[0]; $this->prop2 = $data[1]; if (!$this->prop) { throw new RuntimeException("Threw from __unserialize"); } } public function __destruct() { // should not be called echo "Called destruct\n"; } } $test = new Test; $test->prop = &$test; $test->prop2 = [&$test]; var_dump(bin2hex($s = igbinary_serialize($test))); var_dump(igbinary_unserialize($s)); unset($test->prop); $test->prop = false; var_dump(bin2hex($s = igbinary_serialize($test))); try { igbinary_unserialize($s); } catch (RuntimeException $e) { echo "message={$e->getMessage()}\n"; } echo "Calling gc_collect_cycles\n"; gc_collect_cycles(); echo "After call to gc_collect_cycles\n"; ?> --EXPECT-- string(50) "00000002170454657374140206002200060114010600252200" object(Test)#2 (2) { ["prop"]=> *RECURSION* ["prop2"]=> array(1) { [0]=> *RECURSION* } } string(48) "000000021704546573741402060004060114010600252200" message=Threw from __unserialize Calling gc_collect_cycles Called destruct After call to gc_collect_cycles Called destructigbinary-3.2.13/tests/__serialize_011.phpt0000664000175000017500000000534014366750307017442 0ustar tysontyson--TEST-- __unserializing deeply nested structures --SKIPIF-- --FILE-- childs]; } public function __unserialize(array $data) { list($this->childs) = $data; } } function createTree ($width, $depth) { $root = new Node(); $nextLevel = [$root]; for ($level=1; $level<$depth; $level++) { $levelRoots = $nextLevel; $nextLevel = []; while (count($levelRoots) > 0) { $levelRoot = array_shift($levelRoots); for ($w = 0; $w < $width; $w++) { $tester = new Node(); $levelRoot->childs[] = $tester; $nextLevel[] = $tester; } } } return $root; } $width = 3; ob_implicit_flush(); foreach (range(1, 8) as $depth) { $tree = createTree($width, $depth); echo "Testcase tree $width x $depth".PHP_EOL; echo "> Serializing now".PHP_EOL; $serialized = igbinary_serialize($tree); echo "> Unserializing now".PHP_EOL; $tree = igbinary_unserialize($serialized); // Lets test whether all is ok! $expectedSize = ($width**$depth - 1)/($width-1); $nodes = [$tree]; $count = 0; while (count($nodes) > 0) { $count++; $node = array_shift($nodes); foreach ($node->childs as $node) { $nodes[] = $node; } } echo "> Unserialized total node count was $count, expected $expectedSize: ".($expectedSize === $count ? 'CORRECT!' : 'INCORRECT'); echo PHP_EOL; echo PHP_EOL; } ?> --EXPECT-- Testcase tree 3 x 1 > Serializing now > Unserializing now > Unserialized total node count was 1, expected 1: CORRECT! Testcase tree 3 x 2 > Serializing now > Unserializing now > Unserialized total node count was 4, expected 4: CORRECT! Testcase tree 3 x 3 > Serializing now > Unserializing now > Unserialized total node count was 13, expected 13: CORRECT! Testcase tree 3 x 4 > Serializing now > Unserializing now > Unserialized total node count was 40, expected 40: CORRECT! Testcase tree 3 x 5 > Serializing now > Unserializing now > Unserialized total node count was 121, expected 121: CORRECT! Testcase tree 3 x 6 > Serializing now > Unserializing now > Unserialized total node count was 364, expected 364: CORRECT! Testcase tree 3 x 7 > Serializing now > Unserializing now > Unserialized total node count was 1093, expected 1093: CORRECT! Testcase tree 3 x 8 > Serializing now > Unserializing now > Unserialized total node count was 3280, expected 3280: CORRECT! igbinary-3.2.13/tests/__serialize_012.phpt0000664000175000017500000000212114366750307017435 0ustar tysontyson--TEST-- Test unserialization of classes derived from ArrayIterator --SKIPIF-- --FILE-- object(Foo1)#3 (1) { ["storage":"ArrayIterator":private]=> array(0) { } } [1]=> object(Foo2)#4 (0) { } } array(2) { [0]=> object(__PHP_Incomplete_Class)#4 (5) { ["__PHP_Incomplete_Class_Name"]=> string(4) "Bar1" ["0"]=> int(0) ["1"]=> array(0) { } ["2"]=> array(0) { } ["3"]=> NULL } [1]=> object(__PHP_Incomplete_Class)#3 (1) { ["__PHP_Incomplete_Class_Name"]=> string(4) "Bar2" } } igbinary-3.2.13/tests/__serialize_013.phpt0000664000175000017500000000543114366750307017445 0ustar tysontyson--TEST-- __serialize() mechanism (013): Properties are still typed after unserialization --SKIPIF-- = 80000) { echo "skip different error message format"; } ?> --FILE-- i = 1; $t->s = 'other'; $t->o = $t; $t->std = (object)['key' => 'value']; $t->a = [$t->std]; var_dump($t); var_dump(bin2hex($s = igbinary_serialize($t))); $t2 = igbinary_unserialize($s); var_dump($t2); try { $t2->i = 'x'; } catch (Error $e) { echo "i: " . $e->getMessage() . "\n"; } $t2->s = null; try { $t2->s = false; } catch (Error $e) { echo "s: " . $e->getMessage() . "\n"; } $t2->s = 'other'; try { $t2->o = null; } catch (Error $e) { echo "o: " . $e->getMessage() . "\n"; } try { $t2->a = null; } catch (Error $e) { echo "a: " . $e->getMessage() . "\n"; } try { $t2->stdClass = $t; } catch (Error $e) { echo "stdClass: " . $e->getMessage() . "\n"; } try { $t2->a = $t2; } catch (Error $e) { echo "a: " . $e->getMessage() . "\n"; } var_dump($t2); --EXPECT-- object(Test)#1 (5) { ["i"]=> int(1) ["s"]=> string(5) "other" ["o"]=> *RECURSION* ["stdClass"]=> uninitialized(stdClass) ["a"]=> array(1) { [0]=> object(stdClass)#2 (1) { ["key"]=> string(5) "value" } } ["std"]=> object(stdClass)#2 (1) { ["key"]=> string(5) "value" } } string(142) "000000021704546573741406110169060111017311056f7468657211016f220000110161140106001708737464436c617373140111036b6579110576616c756511037374642202" object(Test)#3 (5) { ["i"]=> int(1) ["s"]=> string(5) "other" ["o"]=> *RECURSION* ["stdClass"]=> uninitialized(stdClass) ["a"]=> array(1) { [0]=> object(stdClass)#4 (1) { ["key"]=> string(5) "value" } } ["std"]=> object(stdClass)#4 (1) { ["key"]=> string(5) "value" } } i: Typed property Test::$i must be int, string used s: Typed property Test::$s must be string or null, bool used o: Typed property Test::$o must be object, null used a: Typed property Test::$a must be array, null used stdClass: Typed property Test::$stdClass must be an instance of stdClass, Test used a: Typed property Test::$a must be array, Test used object(Test)#3 (5) { ["i"]=> int(1) ["s"]=> string(5) "other" ["o"]=> *RECURSION* ["stdClass"]=> uninitialized(stdClass) ["a"]=> array(1) { [0]=> object(stdClass)#4 (1) { ["key"]=> string(5) "value" } } ["std"]=> object(stdClass)#4 (1) { ["key"]=> string(5) "value" } } igbinary-3.2.13/tests/__serialize_013_php8.phpt0000664000175000017500000000533114366750307020403 0ustar tysontyson--TEST-- __serialize() mechanism (013): Properties are still typed after unserialization (php8) --SKIPIF-- --FILE-- i = 1; $t->s = 'other'; $t->o = $t; $t->std = (object)['key' => 'value']; $t->a = [$t->std]; var_dump($t); var_dump(bin2hex($s = igbinary_serialize($t))); $t2 = igbinary_unserialize($s); var_dump($t2); try { $t2->i = 'x'; } catch (Error $e) { echo "i: " . $e->getMessage() . "\n"; } $t2->s = null; try { $t2->s = false; } catch (Error $e) { echo "s: " . $e->getMessage() . "\n"; } $t2->s = 'other'; try { $t2->o = null; } catch (Error $e) { echo "o: " . $e->getMessage() . "\n"; } try { $t2->a = null; } catch (Error $e) { echo "a: " . $e->getMessage() . "\n"; } try { $t2->stdClass = $t; } catch (Error $e) { echo "stdClass: " . $e->getMessage() . "\n"; } try { $t2->a = $t2; } catch (Error $e) { echo "a: " . $e->getMessage() . "\n"; } var_dump($t2); --EXPECT-- object(Test)#1 (5) { ["i"]=> int(1) ["s"]=> string(5) "other" ["o"]=> *RECURSION* ["stdClass"]=> uninitialized(stdClass) ["a"]=> array(1) { [0]=> object(stdClass)#2 (1) { ["key"]=> string(5) "value" } } ["std"]=> object(stdClass)#2 (1) { ["key"]=> string(5) "value" } } string(142) "000000021704546573741406110169060111017311056f7468657211016f220000110161140106001708737464436c617373140111036b6579110576616c756511037374642202" object(Test)#3 (5) { ["i"]=> int(1) ["s"]=> string(5) "other" ["o"]=> *RECURSION* ["stdClass"]=> uninitialized(stdClass) ["a"]=> array(1) { [0]=> object(stdClass)#4 (1) { ["key"]=> string(5) "value" } } ["std"]=> object(stdClass)#4 (1) { ["key"]=> string(5) "value" } } i: Cannot assign string to property Test::$i of type int s: Cannot assign bool to property Test::$s of type ?string o: Cannot assign null to property Test::$o of type object a: Cannot assign null to property Test::$a of type array stdClass: Cannot assign Test to property Test::$stdClass of type stdClass a: Cannot assign Test to property Test::$a of type array object(Test)#3 (5) { ["i"]=> int(1) ["s"]=> string(5) "other" ["o"]=> *RECURSION* ["stdClass"]=> uninitialized(stdClass) ["a"]=> array(1) { [0]=> object(stdClass)#4 (1) { ["key"]=> string(5) "value" } } ["std"]=> object(stdClass)#4 (1) { ["key"]=> string(5) "value" } }igbinary-3.2.13/tests/__serialize_014.phpt0000664000175000017500000000303714366750307017446 0ustar tysontyson--TEST-- __serialize() mechanism (014): Uninitialized properties can be serialized and unserialized --SKIPIF-- = 80000) { echo "skip different error message format"; } ?> --FILE-- o = new stdClass(); unset($m->o); $m->s = 'other'; unset($m->s); $m->i = 42; unset($m->i); // Should have the same serialized representation. var_dump($m); var_dump(bin2hex($s = igbinary_serialize($m))); try { $m->i = 'i'; } catch (TypeError $e) { echo $e->getMessage() . "\n"; } --EXPECT-- object(MyClass)#1 (0) { ["o"]=> uninitialized(stdClass) ["s"]=> uninitialized(string) ["i"]=> uninitialized(?int) } string(36) "0000000217074d79436c6173731403000000" object(MyClass)#2 (0) { ["o"]=> uninitialized(stdClass) ["s"]=> uninitialized(string) ["i"]=> uninitialized(?int) } object(MyClass)#2 (0) { ["o"]=> uninitialized(stdClass) ["s"]=> uninitialized(string) ["i"]=> uninitialized(?int) } string(36) "0000000217074d79436c6173731403000000" Typed property MyClass::$i must be int or null, string used igbinary-3.2.13/tests/__serialize_014_php8.phpt0000664000175000017500000000266414366750307020412 0ustar tysontyson--TEST-- __serialize() mechanism (014): Uninitialized properties can be serialized and unserialized --SKIPIF-- --FILE-- o = new stdClass(); unset($m->o); $m->s = 'other'; unset($m->s); $m->i = 42; unset($m->i); // Should have the same serialized representation. var_dump($m); var_dump(bin2hex($s = igbinary_serialize($m))); try { $m->i = 'i'; } catch (TypeError $e) { echo $e->getMessage() . "\n"; } --EXPECT-- object(MyClass)#1 (0) { ["o"]=> uninitialized(stdClass) ["s"]=> uninitialized(string) ["i"]=> uninitialized(?int) } string(36) "0000000217074d79436c6173731403000000" object(MyClass)#2 (0) { ["o"]=> uninitialized(stdClass) ["s"]=> uninitialized(string) ["i"]=> uninitialized(?int) } object(MyClass)#2 (0) { ["o"]=> uninitialized(stdClass) ["s"]=> uninitialized(string) ["i"]=> uninitialized(?int) } string(36) "0000000217074d79436c6173731403000000" Cannot assign string to property MyClass::$i of type ?intigbinary-3.2.13/tests/__serialize_015.phpt0000664000175000017500000000470214366750307017447 0ustar tysontyson--TEST-- __serialize() mechanism (015): Uninitialized properties from __sleep should throw when serializing --SKIPIF-- --FILE-- $name = $value; } } class SimplePrivate { private ?int $i; public function __sleep() { return ['i']; } public function __set($name, $value) { $this->$name = $value; } } // 00000002 -- header // 17 03 4d79436c617373 -- object of type "MyClass" // 14 03 000000 -- with 3 uninitialized properties $m = new OSI(); function try_serialize_invalid($o) { try { var_dump(bin2hex($s = igbinary_serialize($o))); } catch (Error $e) { printf("Caught %s: %s\n", get_class($e), $e->getMessage()); } } // These should throw whether or not the uninitialized property is nullable. try_serialize_invalid(new OSI()); try_serialize_invalid(new SimplePublic()); try_serialize_invalid(new SimpleProtected()); try_serialize_invalid(new SimplePrivate()); $s = new SimplePublic(); $s->i = null; try_serialize_invalid($s); $s = new SimpleProtected(); $s->i = 0; try_serialize_invalid($s); $s = new SimplePrivate(); $s->i = null; try_serialize_invalid($s); --EXPECT-- Caught Error: Typed property OSI::$o must not be accessed before initialization (in __sleep) Caught Error: Typed property SimplePublic::$i must not be accessed before initialization (in __sleep) Caught Error: Typed property SimpleProtected::$i must not be accessed before initialization (in __sleep) Caught Error: Typed property SimplePrivate::$i must not be accessed before initialization (in __sleep) string(48) "00000002170c53696d706c655075626c6963140111016900" string(62) "00000002170f53696d706c6550726f74656374656414011104002a00690600" string(80) "00000002170d53696d706c6550726976617465140111100053696d706c6550726976617465006900" igbinary-3.2.13/tests/__serialize_016.phpt0000664000175000017500000000673014366750307017453 0ustar tysontyson--TEST-- __serialize() mechanism (016): Properties are still typed after unserialization (references) --SKIPIF-- = 80000) { echo "skip different error message format"; } ?> --FILE-- i = 1; $t->s = 'other'; $t->o = $t; $t->std = (object)['key' => 'value']; $t->a = [&$t->std, &$t->i, &$t->s, &$t->o]; $t->std->key = &$t->a; var_dump($t); var_dump(bin2hex($s = igbinary_serialize($t))); $t2 = igbinary_unserialize($s); var_dump($t2); try { $t2->i = 'x'; } catch (Error $e) { echo "i: " . $e->getMessage() . "\n"; } $t2->s = null; try { $t2->s = false; } catch (Error $e) { echo "s: " . $e->getMessage() . "\n"; } $t2->s = 'other'; try { $t2->o = null; } catch (Error $e) { echo "o: " . $e->getMessage() . "\n"; } try { $t2->a = null; } catch (Error $e) { echo "a: " . $e->getMessage() . "\n"; } try { $t2->stdClass = $t; } catch (Error $e) { echo "stdClass: " . $e->getMessage() . "\n"; } try { $t2->a = $t2; } catch (Error $e) { echo "a: " . $e->getMessage() . "\n"; } var_dump($t2); --EXPECT-- object(Test)#1 (5) { ["i"]=> &int(1) ["s"]=> &string(5) "other" ["o"]=> *RECURSION* ["stdClass"]=> uninitialized(stdClass) ["a"]=> &array(4) { [0]=> &object(stdClass)#2 (1) { ["key"]=> *RECURSION* } [1]=> &int(1) [2]=> &string(5) "other" [3]=> *RECURSION* } ["std"]=> &object(stdClass)#2 (1) { ["key"]=> &array(4) { [0]=> *RECURSION* [1]=> &int(1) [2]=> &string(5) "other" [3]=> *RECURSION* } } } string(176) "0000000217045465737414061101692506011101732511056f7468657211016f252200001101612514040600251708737464436c617373140111036b65792501030601250101060225010206032522001103737464252204" object(Test)#3 (5) { ["i"]=> &int(1) ["s"]=> &string(5) "other" ["o"]=> *RECURSION* ["stdClass"]=> uninitialized(stdClass) ["a"]=> &array(4) { [0]=> &object(stdClass)#4 (1) { ["key"]=> *RECURSION* } [1]=> &int(1) [2]=> &string(5) "other" [3]=> *RECURSION* } ["std"]=> &object(stdClass)#4 (1) { ["key"]=> &array(4) { [0]=> *RECURSION* [1]=> &int(1) [2]=> &string(5) "other" [3]=> *RECURSION* } } } i: Typed property Test::$i must be int, string used s: Typed property Test::$s must be string or null, bool used o: Typed property Test::$o must be object, null used a: Typed property Test::$a must be array, null used stdClass: Typed property Test::$stdClass must be an instance of stdClass, Test used a: Typed property Test::$a must be array, Test used object(Test)#3 (5) { ["i"]=> &int(1) ["s"]=> &string(5) "other" ["o"]=> *RECURSION* ["stdClass"]=> uninitialized(stdClass) ["a"]=> &array(4) { [0]=> &object(stdClass)#4 (1) { ["key"]=> *RECURSION* } [1]=> &int(1) [2]=> &string(5) "other" [3]=> *RECURSION* } ["std"]=> &object(stdClass)#4 (1) { ["key"]=> &array(4) { [0]=> *RECURSION* [1]=> &int(1) [2]=> &string(5) "other" [3]=> *RECURSION* } } }igbinary-3.2.13/tests/__serialize_016_php8.phpt0000664000175000017500000000716014366750307020410 0ustar tysontyson--TEST-- __serialize() mechanism (016): Properties are still typed after unserialization (references) --SKIPIF-- = 90000) { echo "skip requires php < 9.0 when testing that the deprecation has no impact on igbinary functionality\n"; } ?> --FILE-- = 80200) { require_once __DIR__ . '/php82_suppress_dynamic_properties_warning.inc'; } class Test { public int $i = 0; public ?string $s = 's'; public object $o; public stdClass $stdClass; public array $a = []; } $t = new Test(); $t->i = 1; $t->s = 'other'; $t->o = $t; $t->std = (object)['key' => 'value']; $t->a = [&$t->std, &$t->i, &$t->s, &$t->o]; $t->std->key = &$t->a; var_dump($t); var_dump(bin2hex($s = igbinary_serialize($t))); $t2 = igbinary_unserialize($s); var_dump($t2); try { $t2->i = 'x'; } catch (Error $e) { echo "i: " . $e->getMessage() . "\n"; } $t2->s = null; try { $t2->s = false; } catch (Error $e) { echo "s: " . $e->getMessage() . "\n"; } $t2->s = 'other'; try { $t2->o = null; } catch (Error $e) { echo "o: " . $e->getMessage() . "\n"; } try { $t2->a = null; } catch (Error $e) { echo "a: " . $e->getMessage() . "\n"; } try { $t2->stdClass = $t; } catch (Error $e) { echo "stdClass: " . $e->getMessage() . "\n"; } try { $t2->a = $t2; } catch (Error $e) { echo "a: " . $e->getMessage() . "\n"; } var_dump($t2); --EXPECT-- object(Test)#1 (5) { ["i"]=> &int(1) ["s"]=> &string(5) "other" ["o"]=> *RECURSION* ["stdClass"]=> uninitialized(stdClass) ["a"]=> &array(4) { [0]=> &object(stdClass)#2 (1) { ["key"]=> *RECURSION* } [1]=> &int(1) [2]=> &string(5) "other" [3]=> *RECURSION* } ["std"]=> &object(stdClass)#2 (1) { ["key"]=> &array(4) { [0]=> *RECURSION* [1]=> &int(1) [2]=> &string(5) "other" [3]=> *RECURSION* } } } string(176) "0000000217045465737414061101692506011101732511056f7468657211016f252200001101612514040600251708737464436c617373140111036b65792501030601250101060225010206032522001103737464252204" object(Test)#3 (5) { ["i"]=> &int(1) ["s"]=> &string(5) "other" ["o"]=> *RECURSION* ["stdClass"]=> uninitialized(stdClass) ["a"]=> &array(4) { [0]=> &object(stdClass)#4 (1) { ["key"]=> *RECURSION* } [1]=> &int(1) [2]=> &string(5) "other" [3]=> *RECURSION* } ["std"]=> &object(stdClass)#4 (1) { ["key"]=> &array(4) { [0]=> *RECURSION* [1]=> &int(1) [2]=> &string(5) "other" [3]=> *RECURSION* } } } i: Cannot assign string to property Test::$i of type int s: Cannot assign bool to property Test::$s of type ?string o: Cannot assign null to property Test::$o of type object a: Cannot assign null to property Test::$a of type array stdClass: Cannot assign Test to property Test::$stdClass of type stdClass a: Cannot assign Test to property Test::$a of type array object(Test)#3 (5) { ["i"]=> &int(1) ["s"]=> &string(5) "other" ["o"]=> *RECURSION* ["stdClass"]=> uninitialized(stdClass) ["a"]=> &array(4) { [0]=> &object(stdClass)#4 (1) { ["key"]=> *RECURSION* } [1]=> &int(1) [2]=> &string(5) "other" [3]=> *RECURSION* } ["std"]=> &object(stdClass)#4 (1) { ["key"]=> &array(4) { [0]=> *RECURSION* [1]=> &int(1) [2]=> &string(5) "other" [3]=> *RECURSION* } } }igbinary-3.2.13/tests/__serialize_017.phpt0000664000175000017500000000164314366750307017452 0ustar tysontyson--TEST-- __serialize() mechanism (001): Basics --SKIPIF-- --FILE-- $this->prop]; } public function __unserialize(array $data) { echo "In __unserialize\n"; if (!$data['prop']) { throw new RuntimeException("bad prop"); } } public function __destruct() { echo "In __destruct\n"; } } $test = new Test; $test->prop = new Test(); var_dump(bin2hex($s = igbinary_serialize($test))); try { igbinary_unserialize($s); } catch (RuntimeException $e) { echo "Caught {$e->getMessage()}\n"; } ?> --EXPECT-- string(50) "000000021704546573741401110470726f701a0014010e0100" In __unserialize Caught bad prop In __destruct In __destructigbinary-3.2.13/tests/__serialize_018.phpt0000664000175000017500000000446414366750307017457 0ustar tysontyson--TEST-- __serialize() freed on unserialize exception without calling destructor. --SKIPIF-- --INI-- ; Note that php 8.1 deprecates using Serializable without __serialize/__unserialize but we are testing Serialize for igbinary. Suppress deprecations. error_reporting=E_ALL & ~E_DEPRECATED --FILE-- $this->prop, "value" => $this->prop2]; } public function __unserialize(array $data) { echo "In __unserialize Test\n"; } public function __destruct() { echo "In __destruct Test\n"; } } $test = new Test; $test->prop = new ThrowsInUnserialize(); $test->prop2 = "barfoo"; var_dump(bin2hex($s = igbinary_serialize($test))); try { var_dump(igbinary_unserialize($s)); } catch (Error $e) { echo "Caught: {$e->getMessage()}\n"; } gc_collect_cycles(); echo "After gc_collect_cycles\n"; unset($test); gc_collect_cycles(); echo "And for serialize/unserialize\n"; $test = new Test; $test->prop = new ThrowsInUnserialize(); $test->prop2 = "barfoo"; var_dump(bin2hex($s = serialize($test))); try { var_dump(unserialize($s)); } catch (Error $e) { echo "Caught: {$e->getMessage()}\n"; } ?> --EXPECT-- string(122) "000000021704546573741402060017135468726f7773496e556e73657269616c697a651d09746573742064617461110576616c75651106626172666f6f" Unserializing test data In __destruct ThrowsInUnserialize Caught: test data After gc_collect_cycles In __destruct Test In __destruct ThrowsInUnserialize And for serialize/unserialize string(168) "4f3a343a2254657374223a323a7b693a303b433a31393a225468726f7773496e556e73657269616c697a65223a393a7b7465737420646174617d733a353a2276616c7565223b733a363a22626172666f6f223b7d" Unserializing test data In __destruct ThrowsInUnserialize Caught: test data In __destruct Test In __destruct ThrowsInUnserializeigbinary-3.2.13/tests/__serialize_019.phpt0000664000175000017500000000251514366750307017453 0ustar tysontyson--TEST-- __serialize() freed on unserialize exception without calling destructor. --SKIPIF-- --FILE-- $this->prop, "value" => $this->prop2]; } public function __unserialize(array $data) { echo "In __unserialize Test\n"; $this->prop = $data[0]; $this->prop2 = $data['value']; unset($data[0]); unset($data['value']); } public function __destruct() { echo "In __destruct Test\n"; } } $obj = new stdClass(); $testObj = new Test(); $testObj->prop = 123; $testObj->prop2 = ['xyz']; $obj->test = 'bar'; $obj->value = &$testObj; var_dump(bin2hex($s = igbinary_serialize($obj))); var_dump(igbinary_unserialize($s)); echo "Done\n"; ?> --EXPECT-- string(116) "000000021708737464436c61737314021104746573741103626172110576616c75652517045465737414020600067b0e0314010600110378797a" In __unserialize Test object(stdClass)#3 (2) { ["test"]=> string(3) "bar" ["value"]=> object(Test)#4 (2) { ["prop"]=> int(123) ["prop2"]=> array(1) { [0]=> string(3) "xyz" } } } In __destruct Test Done In __destruct Test igbinary-3.2.13/tests/__serialize_020.phpt0000664000175000017500000004066514366750307017453 0ustar tysontyson--TEST-- issue when serializing/deserializing nested objects with __serialize --SKIPIF-- --FILE-- events[] = $event; $this->transports[$event->getTransport()] = true; } public function getEvents(string $name = null): array { return $this->events; } } final class MessageEvent extends Event { private $propagationStopped = false; private $message; private $envelope; private $transport; private $queued; public function __construct(RawMessage $message, Envelope $envelope, string $transport, bool $queued = false) { $this->message = $message; $this->envelope = $envelope; $this->transport = $transport; $this->queued = $queued; } public function getTransport(): string { return $this->transport; } public function getMessage(): RawMessage { return $this->message; } } class Envelope { protected $sender; protected $recipients = []; protected $senderSet = false; protected $recipientsSet = false; protected $message; public function __construct(Address $sender, array $recipients) { $this->setSender($sender); $this->setRecipients($recipients); } public static function create(RawMessage $message): self { return new DelayedEnvelope($message); } public function setSender(Address $sender): void { $this->sender = $sender; } public function setRecipients(array $recipients): void { $this->recipients = []; foreach ($recipients as $recipient) { $this->recipients[] = new Address($recipient->getAddress()); } } public function getRecipients(): array { return $this->recipients; } } final class DelayedEnvelope extends Envelope { public function __construct(Message $message) { $this->message = $message; } public function setSender(Address $sender): void { parent::setSender($sender); $this->senderSet = true; } public function setRecipients(array $recipients): void { parent::setRecipients($recipients); $this->recipientsSet = parent::getRecipients(); } } final class Address { private $address; private $name; public function __construct(string $address, string $name = '') { $this->address = trim($address); $this->name = trim(str_replace(["\n", "\r"], '', $name)); } /** * @param Address|string $address */ public static function create($address): self { if ($address instanceof self) { return $address; } if (\is_string($address)) { if (false === strpos($address, '<')) { return new self($address); } return new self($matches['addrSpec'], trim($matches['displayName'], ' \'"')); } throw new InvalidArgumentException(sprintf('An address can be an instance of Address or a string ("%s" given).', get_debug_type($address))); } public static function createArray(array $addresses): array { $addrs = []; foreach ($addresses as $address) { $addrs[] = self::create($address); } return $addrs; } } abstract class AbstractHeader { private static $encoder; private $name; private $lineLength = 76; private $lang; private $charset = 'utf-8'; protected $addresses = []; public function __construct(string $name) { $this->name = $name; } public function getName() { return $this->name; } } final class Headers { private $headers = []; private $lineLength = 76; public function __construct(...$headers) { foreach ($headers as $header) { $this->add($header); } } public function __clone() { foreach ($this->headers as $name => $collection) { foreach ($collection as $i => $header) { $this->headers[$name][$i] = clone $header; } } } public function addMailboxListHeader(string $name, array $addresses): self { return $this->add(new MailboxListHeader($name, Address::createArray($addresses))); } public function add($header): self { self::checkHeaderClass($header); $name = strtolower($header->getName()); $this->headers[$name][] = $header; return $this; } public function get(string $name) { $name = strtolower($name); if (!isset($this->headers[$name])) { return null; } $values = array_values($this->headers[$name]); return array_shift($values); } public function all(string $name = null): iterable { if (null === $name) { foreach ($this->headers as $name => $collection) { foreach ($collection as $header) { yield $name => $header; } } } elseif (isset($this->headers[strtolower($name)])) { foreach ($this->headers[strtolower($name)] as $header) { yield $header; } } } public static function checkHeaderClass($header): void { $name = strtolower($header->getName()); } } final class MailboxListHeader extends AbstractHeader { public function __construct(string $name, array $addresses) { parent::__construct($name); $this->addAddresses($addresses); } public function addAddresses(array $addresses) { foreach ($addresses as $address) { $this->addAddress($address); } } /** * @throws RfcComplianceException */ public function addAddress(Address $address) { $this->addresses[] = $address; } } class RawMessage { protected $message; protected $headers; protected $body; protected $text; protected $textCharset; protected $html; protected $htmlCharset; protected $attachments = []; public function __construct($message) { $this->message = $message; } public function __serialize(): array { return [$this->message]; } public function __unserialize(array $data): void { [$this->message] = $data; } } class Message extends RawMessage { public function __construct(Headers $headers = null, AbstractPart $body = null) { $this->headers = $headers ? clone $headers : new Headers(); $this->body = $body; } public function __clone() { $this->headers = clone $this->headers; if (null !== $this->body) { $this->body = clone $this->body; } } public function getHeaders(): Headers { return $this->headers; } public function __serialize(): array { return [$this->headers]; } public function __unserialize(array $data): void { [$this->headers] = $data; } } class Email extends Message { public function to(...$addresses) { return $this->setListAddressHeaderBody('To', $addresses); } private function setListAddressHeaderBody(string $name, array $addresses) { $addresses = Address::createArray($addresses); $headers = $this->getHeaders(); $headers->addMailboxListHeader($name, $addresses); return $this; } /** * @internal */ public function __serialize(): array { return [$this->text, $this->textCharset, $this->html, $this->htmlCharset, $this->attachments, parent::__serialize()]; } /** * @internal */ public function __unserialize(array $data): void { [$this->text, $this->textCharset, $this->html, $this->htmlCharset, $this->attachments, $parentData] = $data; parent::__unserialize($parentData); } } $messageEvents = new MessageEvents(); $messageEvents->add(new MessageEvent($message1 = (new Email())->to('alice@example.com'), Envelope::create($message1), 'null://null')); $messageEvents->add(new MessageEvent($message2 = (new Email())->to('bob@example.com'), Envelope::create($message2), 'null://null')); var_dump($messageEvents); // Comment/uncomment to trigger the bug var_dump('headers_before', $messageEvents->getEvents()[0]->getMessage()->getHeaders() === $messageEvents->getEvents()[1]->getMessage()->getHeaders()); $ser = igbinary_serialize($messageEvents); $messageEvents = igbinary_unserialize($ser); // should dump "false", but dumps "true" the "var_dump($messageEvents)" is not commented var_dump('headers_after', $messageEvents->getEvents()[0]->getMessage()->getHeaders() === $messageEvents->getEvents()[1]->getMessage()->getHeaders()); ?> --EXPECT-- object(MessageEvents)#1 (2) { ["events":"MessageEvents":private]=> array(2) { [0]=> object(MessageEvent)#2 (5) { ["propagationStopped":"MessageEvent":private]=> bool(false) ["message":"MessageEvent":private]=> object(Email)#3 (8) { ["message":protected]=> NULL ["headers":protected]=> object(Headers)#4 (2) { ["headers":"Headers":private]=> array(1) { ["to"]=> array(1) { [0]=> object(MailboxListHeader)#6 (5) { ["name":"AbstractHeader":private]=> string(2) "To" ["lineLength":"AbstractHeader":private]=> int(76) ["lang":"AbstractHeader":private]=> NULL ["charset":"AbstractHeader":private]=> string(5) "utf-8" ["addresses":protected]=> array(1) { [0]=> object(Address)#5 (2) { ["address":"Address":private]=> string(17) "alice@example.com" ["name":"Address":private]=> string(0) "" } } } } } ["lineLength":"Headers":private]=> int(76) } ["body":protected]=> NULL ["text":protected]=> NULL ["textCharset":protected]=> NULL ["html":protected]=> NULL ["htmlCharset":protected]=> NULL ["attachments":protected]=> array(0) { } } ["envelope":"MessageEvent":private]=> object(DelayedEnvelope)#7 (5) { ["sender":protected]=> NULL ["recipients":protected]=> array(0) { } ["senderSet":protected]=> bool(false) ["recipientsSet":protected]=> bool(false) ["message":protected]=> object(Email)#3 (8) { ["message":protected]=> NULL ["headers":protected]=> object(Headers)#4 (2) { ["headers":"Headers":private]=> array(1) { ["to"]=> array(1) { [0]=> object(MailboxListHeader)#6 (5) { ["name":"AbstractHeader":private]=> string(2) "To" ["lineLength":"AbstractHeader":private]=> int(76) ["lang":"AbstractHeader":private]=> NULL ["charset":"AbstractHeader":private]=> string(5) "utf-8" ["addresses":protected]=> array(1) { [0]=> object(Address)#5 (2) { ["address":"Address":private]=> string(17) "alice@example.com" ["name":"Address":private]=> string(0) "" } } } } } ["lineLength":"Headers":private]=> int(76) } ["body":protected]=> NULL ["text":protected]=> NULL ["textCharset":protected]=> NULL ["html":protected]=> NULL ["htmlCharset":protected]=> NULL ["attachments":protected]=> array(0) { } } } ["transport":"MessageEvent":private]=> string(11) "null://null" ["queued":"MessageEvent":private]=> bool(false) } [1]=> object(MessageEvent)#8 (5) { ["propagationStopped":"MessageEvent":private]=> bool(false) ["message":"MessageEvent":private]=> object(Email)#9 (8) { ["message":protected]=> NULL ["headers":protected]=> object(Headers)#10 (2) { ["headers":"Headers":private]=> array(1) { ["to"]=> array(1) { [0]=> object(MailboxListHeader)#12 (5) { ["name":"AbstractHeader":private]=> string(2) "To" ["lineLength":"AbstractHeader":private]=> int(76) ["lang":"AbstractHeader":private]=> NULL ["charset":"AbstractHeader":private]=> string(5) "utf-8" ["addresses":protected]=> array(1) { [0]=> object(Address)#11 (2) { ["address":"Address":private]=> string(15) "bob@example.com" ["name":"Address":private]=> string(0) "" } } } } } ["lineLength":"Headers":private]=> int(76) } ["body":protected]=> NULL ["text":protected]=> NULL ["textCharset":protected]=> NULL ["html":protected]=> NULL ["htmlCharset":protected]=> NULL ["attachments":protected]=> array(0) { } } ["envelope":"MessageEvent":private]=> object(DelayedEnvelope)#13 (5) { ["sender":protected]=> NULL ["recipients":protected]=> array(0) { } ["senderSet":protected]=> bool(false) ["recipientsSet":protected]=> bool(false) ["message":protected]=> object(Email)#9 (8) { ["message":protected]=> NULL ["headers":protected]=> object(Headers)#10 (2) { ["headers":"Headers":private]=> array(1) { ["to"]=> array(1) { [0]=> object(MailboxListHeader)#12 (5) { ["name":"AbstractHeader":private]=> string(2) "To" ["lineLength":"AbstractHeader":private]=> int(76) ["lang":"AbstractHeader":private]=> NULL ["charset":"AbstractHeader":private]=> string(5) "utf-8" ["addresses":protected]=> array(1) { [0]=> object(Address)#11 (2) { ["address":"Address":private]=> string(15) "bob@example.com" ["name":"Address":private]=> string(0) "" } } } } } ["lineLength":"Headers":private]=> int(76) } ["body":protected]=> NULL ["text":protected]=> NULL ["textCharset":protected]=> NULL ["html":protected]=> NULL ["htmlCharset":protected]=> NULL ["attachments":protected]=> array(0) { } } } ["transport":"MessageEvent":private]=> string(11) "null://null" ["queued":"MessageEvent":private]=> bool(false) } } ["transports":"MessageEvents":private]=> array(1) { ["null://null"]=> bool(true) } } string(14) "headers_before" bool(false) string(13) "headers_after" bool(false)igbinary-3.2.13/tests/__serialize_021.phpt0000664000175000017500000000203014366750307017434 0ustar tysontyson--TEST-- __serialize() mechanism (021): Temporary references in serialized arrays should be properly deduplicated --FILE-- i = $i; } public function __serialize() { $j = $this->i + 0x7700; return [&$j, &$j]; } public function __unserialize(array $data) { list($this->i) = $data; } } $values = []; for ($i = 0; $i < 256; $i++) { $values[] = new Test($i); } $ser = igbinary_serialize($values); $result = igbinary_unserialize($ser); printf("Count=%d\n", count($values)); $j = 0; foreach ($values as $i => $v) { if ($i !== $j) { echo "Unexpected key $i, expected $j\n"; } if ($v->i !== $j) { echo "Unexpected value {$v->i}, expected $j\n"; } $j++; } echo "Done\n"; ?> --EXPECT-- Count=256 Done igbinary-3.2.13/tests/__serialize_022.phpt0000664000175000017500000000177714366750307017456 0ustar tysontyson--TEST-- __serialize() mechanism (021): Test __serialize without __unserialize --SKIPIF-- --FILE-- 'value']; public function __serialize() { return self::SOME_CONST; } public static function test_serialize() { $x = igbinary_serialize([self::SOME_CONST, new self(), new self()]); // aggressively reuses arrays echo urlencode($x), "\n"; var_dump(igbinary_unserialize($x)); } } Test::test_serialize(); ?> --EXPECT-- %00%00%00%02%14%03%06%00%14%01%11%03key%11%05value%06%01%17%04Test%14%01%0E%00%0E%01%06%02%1A%02%14%01%0E%00%0E%01 array(3) { [0]=> array(1) { ["key"]=> string(5) "value" } [1]=> object(Test)#2 (1) { ["key"]=> string(5) "value" } [2]=> object(Test)#1 (1) { ["key"]=> string(5) "value" } } igbinary-3.2.13/tests/igbinary_001.phpt0000664000175000017500000000105314366750307016755 0ustar tysontyson--TEST-- Check for igbinary presence --SKIPIF-- --FILE-- --EXPECT-- igbinary extension is available igbinary-3.2.13/tests/igbinary_002.phpt0000664000175000017500000000132014366750307016753 0ustar tysontyson--TEST-- Check for null serialisation --SKIPIF-- --FILE-- --EXPECT-- null 00 OK igbinary-3.2.13/tests/igbinary_003.phpt0000664000175000017500000000201414366750307016755 0ustar tysontyson--TEST-- Check for bool serialisation --INI-- ; Note that php 8.1 deprecates using Serializable without __serialize/__unserialize but we are testing Serialize for igbinary. Suppress deprecations. error_reporting=E_ALL & ~E_DEPRECATED --FILE-- --EXPECT-- bool true 05 OK bool false 04 OK igbinary-3.2.13/tests/igbinary_004.phpt0000664000175000017500000000212714366750307016763 0ustar tysontyson--TEST-- Check for integer serialisation --SKIPIF-- --FILE-- --EXPECT-- zero: 0 0600 OK small: 1 0601 OK small: -1 0701 OK medium: 1000 0803e8 OK medium: -1000 0903e8 OK large: 100000 0a000186a0 OK large: -100000 0b000186a0 OK igbinary-3.2.13/tests/igbinary_005.phpt0000664000175000017500000000150714366750307016765 0ustar tysontyson--TEST-- Check for double serialisation --SKIPIF-- --FILE-- --EXPECT-- double: 123.456 0c405edd2f1a9fbe77 OK igbinary-3.2.13/tests/igbinary_006.phpt0000664000175000017500000000156614366750307016773 0ustar tysontyson--TEST-- Check for simple string serialization --SKIPIF-- --FILE-- --EXPECT-- empty: "" 0d OK string: "foobar" 1106666f6f626172 OK igbinary-3.2.13/tests/igbinary_007.phpt0000664000175000017500000000216114366750307016764 0ustar tysontyson--TEST-- Check for simple array serialization --SKIPIF-- --FILE-- --EXPECT-- empty array: 1400 OK array(1, 2, 3) 1403060006010601060206020603 OK array(array(1, 2, 3), arr... 1403060014030600060106010602060206030601140306000604060106050602060606021403060006070601060806020609 OK igbinary-3.2.13/tests/igbinary_008.phpt0000664000175000017500000000203614366750307016766 0ustar tysontyson--TEST-- Check for array+string serialization --SKIPIF-- --FILE-- 1, "two" => 2))', array("one" => 1, "two" => 2)); test('array("kek" => "lol", "lol" => "kek")', array("kek" => "lol", "lol" => "kek")); test('array("" => "empty")', array("" => "empty")); ?> --EXPECT-- array("foo", "foo", "foo") 140306001103666f6f06010e0006020e00 OK array("one" => 1, "two" => 2)) 140211036f6e650601110374776f0602 OK array("kek" => "lol", "lol" => "kek") 140211036b656b11036c6f6c0e010e00 OK array("" => "empty") 14010d1105656d707479 OK igbinary-3.2.13/tests/igbinary_009.phpt0000664000175000017500000000270514366750307016772 0ustar tysontyson--TEST-- Check for reference serialisation --SKIPIF-- 7) { echo "skip requires php 7.x\n"; } --INI-- pcre.jit=0 --FILE-- &array(1) { [0]=> array(1) { [0]=> *RECURSION* } } } Expected array(1) { [0]=> &array(1) { [0]=> *RECURSION* } } (Was normalized) cyclic $a = array(array(&$a)); $a[0] - testing functionality 14010600251401060014010600250101 OK But var dump differs: Actual: array(1) { [0]=> &array(1) { [0]=> array(1) { [0]=> *RECURSION* } } } Expected array(1) { [0]=> &array(1) { [0]=> *RECURSION* } } (Was normalized)igbinary-3.2.13/tests/igbinary_010.phpt0000664000175000017500000000166714366750307016770 0ustar tysontyson--TEST-- Array test --SKIPIF-- --FILE-- array( 'b' => 'c', 'd' => 'e' ), 'f' => array( 'g' => 'h' ) ); test('array', $a, false); /* * you can add regression tests for your extension here * * the output of your test code has to be equal to the * text in the --EXPECT-- section below for the tests * to pass, differences between the output and the * expected text are interpreted as failure * * see TESTING.md for further information on * writing regression tests */ ?> --EXPECT-- array 140211016114021101621101631101641101651101661401110167110168 OK igbinary-3.2.13/tests/igbinary_012.phpt0000664000175000017500000000243114366750307016760 0ustar tysontyson--TEST-- Object test --INI-- ; Note that php 8.1 deprecates using Serializable without __serialize/__unserialize but we are testing Serialize for igbinary. Suppress deprecations. error_reporting=E_ALL & ~E_DEPRECATED --FILE-- a = $a; $this->b = $b; $this->c = $c; } } $o = new Obj(1, 2, 3); test('object', $o); /* * you can add regression tests for your extension here * * the output of your test code has to be equal to the * text in the --EXPECT-- section below for the tests * to pass, differences between the output and the * expected text are interpreted as failure * * see TESTING.md for further information on * writing regression tests */ ?> --EXPECT-- object 17034f626a140311016106011104002a006206021106004f626a00630603 OK igbinary-3.2.13/tests/igbinary_013.phpt0000664000175000017500000000200014366750307016751 0ustar tysontyson--TEST-- Object-Array test --SKIPIF-- --FILE-- a = $a; $this->b = $b; } } $o = array(new Obj(1, 2), new Obj(3, 4)); test('object', $o, false); /* * you can add regression tests for your extension here * * the output of your test code has to be equal to the * text in the --EXPECT-- section below for the tests * to pass, differences between the output and the * expected text are interpreted as failure * * see TESTING.md for further information on * writing regression tests */ ?> --EXPECT-- object 1402060017034f626a14021101610601110162060206011a0014020e0106030e020604 OK igbinary-3.2.13/tests/igbinary_014.phpt0000664000175000017500000000122614366750307016763 0ustar tysontyson--TEST-- Object-Reference test --SKIPIF-- --FILE-- a = $a; $this->b = $b; } } $o = new Obj(1, 2); $a = array(&$o, &$o); test('object', $a, false); --EXPECT-- object 140206002517034f626a1402110161060111016206020601252201 OK igbinary-3.2.13/tests/igbinary_015.phpt0000664000175000017500000000251514366750307016766 0ustar tysontyson--TEST-- Check for serialization handler --SKIPIF-- --EXPECT-- 2 14011103666f6f0602 igbinary-3.2.13/tests/igbinary_015b.phpt0000664000175000017500000000254314366750307017131 0ustar tysontyson--TEST-- Check for serialization handler, ini-directive --SKIPIF-- --EXPECT-- 2 14011103666f6f0602 igbinary-3.2.13/tests/igbinary_015c.phpt0000664000175000017500000000254414366750307017133 0ustar tysontyson--TEST-- Check for serialization handler --SKIPIF-- --FILE-- --EXPECT-- read(abcdef10231512dfaz_12311) write(abcdef10231512dfaz_12311): data:(000000021400) igbinary-3.2.13/tests/igbinary_016.phpt0000664000175000017500000000226514366750307016771 0ustar tysontyson--TEST-- Object test, __sleep --SKIPIF-- --FILE-- a = $a; $this->b = $b; $this->c = $c; $this->d = $d; } function __sleep() { return array('a', 'b', 'c'); } # function __wakeup() { # $this->d = $this->a + $this->b + $this->c; # } } $o = new Obj(1, 2, 3, 4); test('object', $o, true); /* * you can add regression tests for your extension here * * the output of your test code has to be equal to the * text in the --EXPECT-- section below for the tests * to pass, differences between the output and the * expected text are interpreted as failure * * see TESTING.md for further information on * writing regression tests */ ?> --EXPECT-- object 17034f626a140311016106011104002a006206021106004f626a00630603 OK igbinary-3.2.13/tests/igbinary_017.phpt0000664000175000017500000000177314366750307016775 0ustar tysontyson--TEST-- Object test, __wakeup --SKIPIF-- --FILE-- b == 3 ? 'OK' : 'ERROR'; echo "\n"; } class Obj { var $a; var $b; function __construct($a, $b) { $this->a = $a; $this->b = $b; } function __wakeup() { $this->b = $this->a * 3; } } $o = new Obj(1, 2); test('object', $o, false); /* * you can add regression tests for your extension here * * the output of your test code has to be equal to the * text in the --EXPECT-- section below for the tests * to pass, differences between the output and the * expected text are interpreted as failure * * see TESTING.md for further information on * writing regression tests */ ?> --EXPECT-- object 17034f626a140211016106011101620602 OK igbinary-3.2.13/tests/igbinary_018.phpt0000664000175000017500000000251014366750307016764 0ustar tysontyson--TEST-- Object test, __sleep error cases --SKIPIF-- --FILE-- a = $a; $this->b = $b; } function __sleep() { return array('c'); } # function __wakeup() { # $this->b = $this->a * 3; # } } class Opj { var $a; var $b; function __construct($a, $b) { $this->a = $a; $this->b = $b; } function __sleep() { return array(1); } # function __wakeup() { # # } } $o = new Obj(1, 2); $p = new Opj(1, 2); test('nonexisting', $o, true); test('wrong', $p, true); /* * you can add regression tests for your extension here * * the output of your test code has to be equal to the * text in the --EXPECT-- section below for the tests * to pass, differences between the output and the * expected text are interpreted as failure * * see TESTING.md for further information on * writing regression tests */ ?> --EXPECT-- nonexisting 17034f626a140111016300 OK wrong 17034f706a140100 OK igbinary-3.2.13/tests/igbinary_019.phpt0000664000175000017500000000205614366750307016772 0ustar tysontyson--TEST-- Object test, __autoload --SKIPIF-- --FILE-- b == 2 ? 'OK' : 'ERROR'; echo "\n"; } function test_autoload($classname) { class Obj { var $a; var $b; function __construct($a, $b) { $this->a = $a; $this->b = $b; } } } spl_autoload_register("test_autoload"); test('autoload', '0000000217034f626a140211016106011101620602', false); /* * you can add regression tests for your extension here * * the output of your test code has to be equal to the * text in the --EXPECT-- section below for the tests * to pass, differences between the output and the * expected text are interpreted as failure * * see TESTING.md for further information on * writing regression tests */ ?> --EXPECT-- autoload 17034f626a140211016106011101620602 OK igbinary-3.2.13/tests/igbinary_020.phpt0000664000175000017500000000220514366750307016756 0ustar tysontyson--TEST-- Object test, incomplete class --SKIPIF-- --FILE-- = 80200) { // TODO undo workaround when #[AllowDynamicProperties] is added to __PHP_Incomplete_Class error_reporting(E_ALL & ~E_DEPRECATED); } function test($type, $variable, $test) { $serialized = pack('H*', $variable); $unserialized = igbinary_unserialize($serialized); echo $type, "\n"; echo substr(bin2hex($serialized), 8), "\n"; var_dump($unserialized); // echo "\n"; } test('incom', '0000000217034f626a140211016106011101620602', false); /* * you can add regression tests for your extension here * * the output of your test code has to be equal to the * text in the --EXPECT-- section below for the tests * to pass, differences between the output and the * expected text are interpreted as failure * * see TESTING.md for further information on * writing regression tests */ ?> --EXPECTF-- incom 17034f626a140211016106011101620602 object(__PHP_Incomplete_Class)#%d (3) { ["__PHP_Incomplete_Class_Name"]=> string(3) "Obj" ["a"]=> int(1) ["b"]=> int(2) } igbinary-3.2.13/tests/igbinary_021.phpt0000664000175000017500000000254614366750307016767 0ustar tysontyson--TEST-- Object Serializable interface --INI-- ; Note that php 8.1 deprecates using Serializable without __serialize/__unserialize but we are testing Serialize for igbinary. Suppress deprecations. error_reporting=E_ALL & ~E_DEPRECATED --FILE-- a = $a; $this->b = $b; } public function serialize() { return pack('NN', $this->a, $this->b); } public function unserialize($serialized) { $tmp = unpack('N*', $serialized); $this->__construct($tmp[1], $tmp[2]); } } $o = new Obj(1, 2); test('object', $o, false); /* * you can add regression tests for your extension here * * the output of your test code has to be equal to the * text in the --EXPECT-- section below for the tests * to pass, differences between the output and the * expected text are interpreted as failure * * see TESTING.md for further information on * writing regression tests */ ?> --EXPECT-- object 17034f626a1d080000000100000002 OK igbinary-3.2.13/tests/igbinary_022.phpt0000664000175000017500000000373714366750307016773 0ustar tysontyson--TEST-- Object test, unserialize_callback_func (PHP < 8.2) --SKIPIF-- = 80200) echo "skip requires php < 8.2\n"; ?> --INI-- error_reporting=E_ALL unserialize_callback_func=autoload --FILE-- b == 2 ? 'OK' : 'ERROR'; echo "\n"; } function autoload($classname) { echo "Autoloading $classname\n"; if (!class_exists(Obj::class)) { class Obj { var $a; var $b; function __construct($a, $b) { $this->a = $a; $this->b = $b; } } } } test('autoload', '0000000217034f626a140211016106011101620602', false); test('autoload', '0000000217034f626b140211016106011101620602', false); ini_set('unserialize_callback_func', strtolower('Missing_autoload')); test('missing_autoload', '0000000217034f626c140211016106011101620602', false); /* * you can add regression tests for your extension here * * the output of your test code has to be equal to the * text in the --EXPECT-- section below for the tests * to pass, differences between the output and the * expected text are interpreted as failure * * see TESTING.md for further information on * writing regression tests */ ?> --EXPECTF-- Autoloading Obj autoload 17034f626a140211016106011101620602 OK Autoloading Obk %s: igbinary_unserialize(): Function autoload() hasn't defined the class it was called for in %sigbinary_022.php on line 8 autoload 17034f626b140211016106011101620602 %s: test(): %s to load the class definition %Sin %sigbinary_022.php on line 12 ERROR %s: igbinary_unserialize(): defined (missing_autoload) but not found in %sigbinary_022.php on line 8 missing_autoload 17034f626c140211016106011101620602 %s: test(): %s to load the class definition %Sin %sigbinary_022.php on line 12 ERROR igbinary-3.2.13/tests/igbinary_022_php82.phpt0000664000175000017500000000566514366750307020016 0ustar tysontyson--TEST-- Object test, unserialize_callback_func (PHP >= 8.2) --SKIPIF-- --INI-- error_reporting=E_ALL unserialize_callback_func=autoload --FILE-- getMessage()}\n"; return; } echo $type, "\n"; echo substr(bin2hex($serialized), 8), "\n"; var_dump($unserialized); // The read_property handler is set to incomplete_class_get_property, // making this always return null for __PHP_Incomplete_Class var_dump($unserialized->b); echo $unserialized->b == 2 ? 'OK' : 'ERROR'; echo "\n"; } function autoload($classname) { echo "Autoloading $classname\n"; if (!class_exists(Obj::class)) { class Obj { var $a; var $b; function __construct($a, $b) { $this->a = $a; $this->b = $b; } } } } test('autoload', '0000000217034f626a140211016106011101620602'); // "Obj" test('autoload', '0000000217034f626b140211016106011101620602'); // "Obk" failing to autoload ini_set('unserialize_callback_func', strtolower('Missing_autoload')); test('missing_autoload', '0000000217034f626c140211016106011101620602'); // Obk" with missing autoload function /* * you can add regression tests for your extension here * * the output of your test code has to be equal to the * text in the --EXPECT-- section below for the tests * to pass, differences between the output and the * expected text are interpreted as failure * * see TESTING.md for further information on * writing regression tests */ ?> --EXPECTF-- Autoloading Obj autoload 17034f626a140211016106011101620602 object(Obj)#1 (2) { ["a"]=> int(1) ["b"]=> int(2) } int(2) OK Autoloading Obk Warning: igbinary_unserialize(): Function autoload() hasn't defined the class it was called for in %sigbinary_022_php82.php on line 9 autoload 17034f626b140211016106011101620602 object(__PHP_Incomplete_Class)#1 (3) { ["__PHP_Incomplete_Class_Name"]=> string(3) "Obk" ["a"]=> int(1) ["b"]=> int(2) } Warning: test(): The script tried to access a property on an incomplete object. Please ensure that the class definition "Obk" of the object you are trying to operate on was loaded _before_ unserialize() gets called or provide an autoloader to load the class definition in %sigbinary_022_php82.php on line 21 NULL Warning: test(): The script tried to access a property on an incomplete object. Please ensure that the class definition "Obk" of the object you are trying to operate on was loaded _before_ unserialize() gets called or provide an autoloader to load the class definition in %sigbinary_022_php82.php on line 22 ERROR missing_autoload Caught Invalid callback missing_autoload, function "missing_autoload" not found or invalid function name igbinary-3.2.13/tests/igbinary_023.phpt0000664000175000017500000000174714366750307016773 0ustar tysontyson--TEST-- Resource --SKIPIF-- a = $a; $this->b = $b; $this->c = $c; } } class Obj2 { public $aa; protected $bb; private $cc; private $obj; function __construct($a, $b, $c) { $this->a = $a; $this->b = $b; $this->c = $c; $this->obj = new Obj($a, $b, $c); } } class Obj3 { private $objs; function __construct($a, $b, $c) { $this->objs = array(); for ($i = $a; $i < $c; $i += $b) { $this->objs[] = new Obj($a, $i, $c); } } } class Obj4 { private $a; private $obj; function __construct($a) { $this->a = $a; } public function set($obj) { $this->obj = $obj; } } $o2 = new Obj2(1, 2, 3); test('objectrec', $o2, false); $o3 = new Obj3(0, 1, 4); test('objectrecarr', $o3, false); $o4 = new Obj4(100); $o4->set($o4); test('objectselfrec', $o4, true); /* * you can add regression tests for your extension here * * the output of your test code has to be equal to the * text in the --EXPECT-- section below for the tests * to pass, differences between the output and the * expected text are interpreted as failure * * see TESTING.md for further information on * writing regression tests */ ?> --EXPECT-- objectrec 17044f626a32140711026161001105002a006262001108004f626a32006363001109004f626a32006f626a17034f626a140311016106011104002a006206021106004f626a006306030e06060111016206021101630603 OK objectrecarr 17044f626a331401110a004f626a33006f626a731404060017034f626a140311016106001104002a006206001106004f626a0063060406011a0214030e0306000e0406010e05060406021a0214030e0306000e0406020e05060406031a0214030e0306000e0406030e050604 OK objectselfrec 17044f626a3414021107004f626a34006106641109004f626a34006f626a2200 OK igbinary-3.2.13/tests/igbinary_025.phpt0000664000175000017500000000400514366750307016763 0ustar tysontyson--TEST-- Object test, array of objects with __sleep --SKIPIF-- --FILE-- a = $a; $this->b = $b; $this->c = $c; $this->d = $d; } function __sleep() { return array('a', 'b', 'c'); } # function __wakeup() { # $this->d = $this->a + $this->b + $this->c; # } } $array = array( new Obj("aa", "bb", "cc", "dd"), new Obj("ee", "ff", "gg", "hh"), new Obj(1, 2, 3, 4), ); test('array', $array, true); ?> --EXPECTREGEX-- array\(3\) { \[0\]=> object\(Obj\)#1 \(4\) { \["a"\]=> string\(2\) "aa" \[("b":protected|"b:protected")\]=> string\(2\) "bb" \["?c"?:("Obj":)?private"?\]=> string\(2\) "cc" \["d"\]=> string\(2\) "dd" } \[1\]=> object\(Obj\)#2 \(4\) { \["a"\]=> string\(2\) "ee" \["?b"?:protected"?\]=> string\(2\) "ff" \["?c"?:("Obj":)?private"?\]=> string\(2\) "gg" \["d"\]=> string\(2\) "hh" } \[2\]=> object\(Obj\)#3 \(4\) { \["a"\]=> int\(1\) \["?b"?:protected"?\]=> int\(2\) \["?c"?:("Obj":)?private"?\]=> int\(3\) \["d"\]=> int\(4\) } } array\(3\) { \[0\]=> object\(Obj\)#4 \(4\) { \["a"\]=> string\(2\) "aa" \["?b"?:protected"?\]=> string\(2\) "bb" \["?c"?:("Obj":)?private"?\]=> string\(2\) "cc" \["d"\]=> NULL } \[1\]=> object\(Obj\)#5 \(4\) { \["a"\]=> string\(2\) "ee" \["?b"?:protected"?\]=> string\(2\) "ff" \["?c"?:("Obj":)?private"?\]=> string\(2\) "gg" \["d"\]=> NULL } \[2\]=> object\(Obj\)#6 \(4\) { \["a"\]=> int\(1\) \["?b"?:protected"?\]=> int\(2\) \["?c"?:("Obj":)?private"?\]=> int\(3\) \["d"\]=> NULL } } igbinary-3.2.13/tests/igbinary_025b.phpt0000664000175000017500000000120714366750307017126 0ustar tysontyson--TEST-- Object test, array of small objects with __sleep --SKIPIF-- --FILE-- c = $c; } function __sleep() { return array('c'); } } $obj = new Obj(4); test('array', $obj, true); ?> --EXPECT-- object(Obj)#1 (1) { ["c":"Obj":private]=> int(4) } object(Obj)#2 (1) { ["c":"Obj":private]=> int(4) } igbinary-3.2.13/tests/igbinary_026.phpt0000664000175000017500000000241214366750307016764 0ustar tysontyson--TEST-- Cyclic array test --INI-- report_memleaks=0 --SKIPIF-- 7) { echo "skip requires php 7.x\n"; } --FILE-- array( 'b' => 'c', 'd' => 'e' ), ); $a['f'] = &$a; test('array', $a, false); $a = array("foo" => &$b); $b = array(1, 2, $a); $exp = $a; $act = igbinary_unserialize(igbinary_serialize($a)); ob_start(); var_dump($exp); $dump_exp = ob_get_clean(); ob_start(); var_dump($act); $dump_act = ob_get_clean(); if ($dump_act !== $dump_exp) { echo "Var dump differs:\n", $dump_act, "\n", $dump_exp, "\n"; } else { echo "Var dump OK\n"; } $act['foo'][1] = 'test value'; $exp['foo'][1] = 'test value'; if ($act['foo'][1] !== $act['foo'][2]['foo'][1]) { echo "Recursive elements differ:\n"; var_dump($act); var_dump($act['foo']); var_dump($exp); var_dump($exp['foo']); } ?> --EXPECT-- array 140211016114021101621101631101641101651101662514020e0001010e05250102 OK Var dump OK igbinary-3.2.13/tests/igbinary_026_php8.phpt0000664000175000017500000000314514366750307017727 0ustar tysontyson--TEST-- Cyclic array test --INI-- report_memleaks=0 --SKIPIF-- array( 'b' => 'c', 'd' => 'e' ), ); $a['f'] = &$a; test('array', $a, false); $a = array("foo" => &$b); $b = array(1, 2, $a); $exp = $a; $act = igbinary_unserialize(igbinary_serialize($a)); ob_start(); var_dump($exp); $dump_exp = ob_get_clean(); ob_start(); var_dump($act); $dump_act = ob_get_clean(); if ($dump_act !== $dump_exp) { echo "Var dump differs:\nActual:\n", $dump_act, "\nExpected:\n", $dump_exp, "\n"; } else { echo "Var dump OK\n"; } $act['foo'][1] = 'test value'; $exp['foo'][1] = 'test value'; if ($act['foo'][1] !== $act['foo'][2]['foo'][1]) { echo "Recursive elements differ:\n"; echo "Actual\n"; var_dump($act); var_dump($act['foo']); echo "Expected\n"; var_dump($exp); var_dump($exp['foo']); } ?> --EXPECT-- array 140211016114021101621101631101641101651101662514020e0001010e05250102 OK Var dump differs: Actual: array(1) { ["foo"]=> &array(3) { [0]=> int(1) [1]=> int(2) [2]=> array(1) { ["foo"]=> *RECURSION* } } } Expected: array(1) { ["foo"]=> &array(3) { [0]=> int(1) [1]=> int(2) [2]=> *RECURSION* } } igbinary-3.2.13/tests/igbinary_026b.phpt0000664000175000017500000000167214366750307017135 0ustar tysontyson--TEST-- Cyclic array test 2 --INI-- report_memleaks=0 --SKIPIF-- 7) { echo "skip requires php 7.x\n"; } --FILE-- &$b); $b = array(1, 2, $a); /* all three statements below should produce same output however PHP stock * unserialize/serialize produces different output (5.2.16). I consider this is * a PHP bug. - Oleg Grenrus */ //$k = $a; //$k = unserialize(serialize($a)); $k = igbinary_unserialize(igbinary_serialize($a)); function check($a, $k) { ob_start(); var_dump($a); $a_str = ob_get_clean(); ob_start(); var_dump($k); $k_str = ob_get_clean(); if ($a_str !== $k_str) { echo "Output differs\n"; echo "Expected:\n", $a_str, "\n"; echo "Actual:\n", $k_str, "\n"; } else { echo "OK\n"; } } check($a, $k); $a["foo"][2]["foo"][1] = "b"; $k["foo"][2]["foo"][1] = "b"; check($a, $k); ?> --EXPECT-- OK OK igbinary-3.2.13/tests/igbinary_026b_php8.phpt0000664000175000017500000000252414366750307020071 0ustar tysontyson--TEST-- Cyclic array test 2 --INI-- report_memleaks=0 --SKIPIF-- &$b); $b = array(1, 2, $a); /* all three statements below should produce same output however PHP stock * unserialize/serialize produces different output (5.2.16). I consider this is * a PHP bug. - Oleg Grenrus */ /* NOTE: This is different in php 8 because igbinary_unserialize() is declared to return a reference, not a value */ //$k = $a; //$k = unserialize(serialize($a)); $k = igbinary_unserialize(igbinary_serialize($a)); function check($a, $k) { ob_start(); var_dump($a); $a_str = ob_get_clean(); ob_start(); var_dump($k); $k_str = ob_get_clean(); if ($a_str !== $k_str) { echo "Output differs\n"; echo "Expected:\n", $a_str, "\n"; echo "Actual:\n", $k_str, "\n"; } else { echo "OK\n"; } } check($a, $k); $a["foo"][2]["foo"][1] = "b"; $k["foo"][2]["foo"][1] = "b"; check($a, $k); ?> --EXPECT-- Output differs Expected: array(1) { ["foo"]=> &array(3) { [0]=> int(1) [1]=> int(2) [2]=> *RECURSION* } } Actual: array(1) { ["foo"]=> &array(3) { [0]=> int(1) [1]=> int(2) [2]=> array(1) { ["foo"]=> *RECURSION* } } } OKigbinary-3.2.13/tests/igbinary_027.phpt0000664000175000017500000000302214366750307016763 0ustar tysontyson--TEST-- Check for serialization handler --SKIPIF-- --EXPECT-- bool(true) read wrote: 14021103666f6f06011104746573741106666f6f626172 igbinary-3.2.13/tests/igbinary_028.phpt0000664000175000017500000000453314366750307016774 0ustar tysontyson--TEST-- Serialize object into session, full set --SKIPIF-- d1 = $foo; $this->d2 = $foo; $this->d3 = $foo; } } class Bar { private static $s1 = array(); protected static $s2 = array(); public static $s3 = array(); public $d1; private $d2; protected $d3; public function __construct() { } public function set($foo) { $this->d1 = $foo; $this->d2 = $foo; $this->d3 = $foo; } } if(!extension_loaded('igbinary')) { dl('igbinary.' . PHP_SHLIB_SUFFIX); } $output = ''; function open($path, $name) { return true; } function close() { return true; } function read($id) { global $output; $output .= "read\n"; $a = new Bar(); $b = new Foo($a); $a->set($b); $session = array('old' => $b); return igbinary_serialize($session); } function write($id, $data) { global $output; $output .= "write: "; $output .= substr(bin2hex($data), 8). "\n"; return true; } function destroy($id) { return true; } function gc($time) { return true; } ini_set('session.serialize_handler', 'igbinary'); session_set_save_handler('open', 'close', 'read', 'write', 'destroy', 'gc'); session_start(); $_SESSION['test'] = "foobar"; $a = new Bar(); $b = new Foo($a); $a->set($b); $_SESSION['new'] = $a; session_write_close(); echo $output; /* * you can add regression tests for your extension here * * the output of your test code has to be equal to the * text in the --EXPECT-- section below for the tests * to pass, differences between the output and the * expected text are interpreted as failure * * see TESTING.md for further information on * writing regression tests */ ?> --EXPECT-- read write: 140311036f6c641703466f6f1403110700466f6f0064311703426172140311026431220111070042617200643222011105002a00643322011105002a00643222021102643322021104746573741106666f6f62617211036e65771a0314030e041a0114030e0222030e0722030e0822030e0522040e062204 igbinary-3.2.13/tests/igbinary_029.phpt0000664000175000017500000000070414366750307016771 0ustar tysontyson--TEST-- Igbinary module info --SKIPIF-- --FILE-- enabled igbinary version => %s igbinary AP%s serializer ABI => %s igbinary session support => %s igbinary.compact_strings => %s => %s igbinary-3.2.13/tests/igbinary_030_php7.phpt0000664000175000017500000000225714366750307017724 0ustar tysontyson--TEST-- Unserialize invalid data --SKIPIF-- = 70200) { echo "Skip php 7.1 or 7.0 required\n"; } ?> --FILE-- {"1"} = "manual"; $datas = array( 87817, -1, array(1,2,3,"testing" => 10, "foo"), true, false, 0.187182, "dakjdh98389\000", null, (object)array(1,2,3), $o, ); error_reporting(0); foreach ($datas as $data) { $str = igbinary_serialize($data); $len = strlen($str); // truncated for ($i = 0; $i < $len - 1; $i++) { $v = igbinary_unserialize(substr($str, 0, $i)); if (is_object($data) && $v !== null && $v == $data) { continue; } elseif ($v !== null && $v != FALSE && $v !== $data) { echo "output at $i:\n"; var_dump($v); echo "vs.\n"; var_dump($data); } } // padded $str2 = $str . "98398afa\000y21_ "; $v = igbinary_unserialize($str2); if ($v !== NULL) { echo "Should return null with padding\n"; var_dump($v); } $str3 = $str . "\x00"; $v = igbinary_unserialize($str3); if ($v !== NULL) { echo "Should return null with single byte of padding\n"; var_dump($v); } } echo "Success!\n"; ?> --EXPECT-- Success! igbinary-3.2.13/tests/igbinary_030_php72.phpt0000664000175000017500000000226314366750307020003 0ustar tysontyson--TEST-- Unserialize invalid data (php 7.2+) --SKIPIF-- --FILE-- {"1"} = "manual"; $datas = array( 87817, -1, array(1,2,3,"testing" => 10, "foo"), true, false, 0.187182, "dakjdh98389\000", null, (object)array(1,2,3), $o, ); error_reporting(0); foreach ($datas as $data) { $str = igbinary_serialize($data); $len = strlen($str); // truncated for ($i = 0; $i < $len - 1; $i++) { $v = igbinary_unserialize(substr($str, 0, $i)); if (is_object($data) && $v !== null && $v == $data) { continue; } elseif ($v !== null && $v != FALSE && $v !== $data) { echo "output at $i:\n"; var_dump($v); echo "vs.\n"; var_dump($data); } } // padded $str2 = $str . "98398afa\000y21_ "; $v = igbinary_unserialize($str2); if ($v !== NULL) { echo "Should return null with padding\n"; var_dump($v); } $str3 = $str . "\x00"; $v = igbinary_unserialize($str3); if ($v !== NULL) { echo "Should return null with single byte of padding\n"; var_dump($v); } } echo "Success!\n"; ?> --EXPECT-- Success! igbinary-3.2.13/tests/igbinary_031.phpt0000664000175000017500000000343314366750307016764 0ustar tysontyson--TEST-- Object Serializable interface throws exceptions --INI-- ; Note that php 8.1 deprecates using Serializable without __serialize/__unserialize but we are testing Serialize for igbinary. Suppress deprecations. error_reporting=E_ALL & ~E_DEPRECATED --FILE-- a = $a; $this->b = $b; } public function serialize() { $c = self::$count++; echo "call serialize, ", ($this->a ? "throw" : "no throw"),"\n"; if ($this->a) { throw new Exception("exception in serialize $c"); } return pack('NN', $this->a, $this->b); } public function unserialize($serialized) { $tmp = unpack('N*', $serialized); $this->__construct($tmp[1], $tmp[2]); $c = self::$count++; echo "call unserialize, ", ($this->b ? "throw" : "no throw"),"\n"; if ($this->b) { throw new Exception("exception in unserialize $c"); } } } $a = new Obj(1, 0); $a = new Obj(0, 0); $b = new Obj(0, 0); $c = new Obj(1, 0); $d = new Obj(0, 1); echo "a, a, c\n"; try { test(array($a, $a, $c)); } catch (Exception $e) { if ($e->getPrevious()) { $e = $e->getPrevious(); } echo $e->getMessage(), "\n"; } echo "b, b, d\n"; try { test(array($b, $b, $d)); } catch (Exception $e) { if ($e->getPrevious()) { $e = $e->getPrevious(); } echo $e->getMessage(), "\n"; } --EXPECT-- a, a, c call serialize, no throw call serialize, throw exception in serialize 2 b, b, d call serialize, no throw call serialize, no throw call unserialize, no throw call unserialize, throw exception in unserialize 6 igbinary-3.2.13/tests/igbinary_032.phpt0000664000175000017500000000257314366750307016771 0ustar tysontyson--TEST-- Object test, __sleep and __wakeup exceptions --SKIPIF-- a = $a; $this->b = $b; } function __sleep() { $c = self::$count++; if ($this->a) { throw new Exception("exception in __sleep $c"); } return array('a', 'b'); } function __wakeup() { $c = self::$count++; if ($this->b) { throw new Exception("exception in __wakeup $c"); } $this->b = $this->a * 3; } } $a = new Obj(1, 0); $b = new Obj(0, 1); $c = new Obj(0, 0); try { test($a); } catch (Exception $e) { echo $e->getMessage(), "\n"; } try { test($b); } catch (Exception $e) { echo $e->getMessage(), "\n"; } try { test($c); } catch (Exception $e) { echo $e->getMessage(), "\n"; } /* * you can add regression tests for your extension here * * the output of your test code has to be equal to the * text in the --EXPECT-- section below for the tests * to pass, differences between the output and the * expected text are interpreted as failure * * see TESTING.md for further information on * writing regression tests */ ?> --EXPECT-- exception in __sleep 0 exception in __wakeup 2 igbinary-3.2.13/tests/igbinary_033.phpt0000664000175000017500000000160414366750307016764 0ustar tysontyson--TEST-- Object test, cyclic references --SKIPIF-- parent = null; $this->children = array(); } public function addChild(Foo $obj) { $this->children[] = $obj; $obj->setParent($this); } public function setParent(Foo $obj) { $this->parent = $obj; } } error_reporting(E_ALL); $obj1 = new Foo(); for ($i = 0; $i < 10; $i++) { $obj = new Foo(); $obj1->addChild($obj); } $o = igbinary_unserialize(igbinary_serialize($obj1->children)); foreach ($obj1->children as $k => $v) { $obj_v = $v; $o_v = $o[$k]; echo gettype($obj_v), "\t", gettype($o_v), "\n"; } --EXPECT-- object object object object object object object object object object object object object object object object object object object object igbinary-3.2.13/tests/igbinary_034.phpt0000664000175000017500000000116314366750307016765 0ustar tysontyson--TEST-- Unserialize invalid random data --SKIPIF-- 10, "foo"), true, false, 0.187182, "dakjdh98389\000", null, (object)array(1,2,3), ); error_reporting(0); foreach ($datas as $data) { $str = igbinary_serialize($data); $len = strlen($str); for ($j = 0; $j < 200; $j++) { for ($i = 0; $i < $len - 1; $i++) { $sub = substr($str, 0, $i); $sub .= mcrypt_create_iv(30, MCRYPT_DEV_URANDOM); $php_errormsg = null; $v = igbinary_unserialize($sub); } } } --EXPECT-- igbinary-3.2.13/tests/igbinary_040.phpt0000664000175000017500000000315714366750307016767 0ustar tysontyson--TEST-- b0rked random data test --SKIPIF-- --FILE-- --EXPECTF-- After testing 100 random values Warning: igbinary_unserialize: received more data to unserialize than expected in %s on line 48 NULL igbinary-3.2.13/tests/igbinary_041.phpt0000664000175000017500000000406314366750307016765 0ustar tysontyson--TEST-- Check for double NaN, Inf, -Inf, 0, and -0. IEEE 754 doubles --FILE-- --INI-- igbinary.compact_strings=Off --FILE-- $object) { if (!isset($actual_array[$key])) { $error = 'ERROR'; echo "Key $key is missing from result.\n"; echo "Expected key/value:\n"; var_dump($key, $object); var_dump($object); break; } if (!is_object($actual_array[$key]) || get_class($object) !== get_class($actual_array[$key])) { $error = 'ERROR'; echo "Array mismatch on $key\n"; echo "Expected key/value:\n"; var_dump($key, $object); echo "Actual key/value:\n"; var_dump($key, $actual_array[$key]); break; } } echo $error, "\n"; --EXPECT-- OK igbinary-3.2.13/tests/igbinary_044.phpt0000664000175000017500000001122014366750307016761 0ustar tysontyson--TEST-- Check for double extremes --FILE-- getVersion(), '4.0.2', '<')) { echo "skip require APCu version 4.0.2 or above"; } --INI-- apc.enable_cli=1 apc.serializer=igbinary --FILE-- int(10) } igbinary-3.2.13/tests/igbinary_045c.phpt0000664000175000017500000000323214366750307017131 0ustar tysontyson--TEST-- APCu serializer registration - more data types --INI-- apc.enable_cli=1 apc.serializer=igbinary --SKIPIF-- getVersion(), '5.1.6') < 0) { echo "skip require APCu version 5.1.6 or above"; return; } ?> --FILE-- foo = $a; apcu_store('objloop', $a); unset($a); var_dump(apcu_fetch('objloop')); apcu_store('nullval', null); var_dump(apcu_fetch('nullval')); apcu_store('intval', 777); var_dump(apcu_fetch('intval')); $o = new stdClass(); $o->prop = 5; $a = [$o, $o]; apcu_store('simplearrayval', $a); $unserialized = apcu_fetch('simplearrayval'); var_dump($unserialized); if ($unserialized[0] === $unserialized[1]) { echo "SAME\n"; } else { echo "DIFFERENT\n"; printf("%s\n", bin2hex(igbinary_serialize($a))); } unset($o); unset($a); unset($unserialized); $o = new stdClass(); $o->prop = 6; $a = [&$o, &$o]; apcu_store('refarrayval', $a); $unserialized = apcu_fetch('refarrayval'); var_dump($unserialized); if ($unserialized[0] === $unserialized[1]) { echo "SAME\n"; } else { echo "DIFFERENT\n"; printf("%s\n", bin2hex(igbinary_serialize($a))); } ?> --EXPECTF-- igbinary object(Bar)#%d (1) { ["foo"]=> *RECURSION* } NULL int(777) array(2) { [0]=> object(stdClass)#%d (1) { ["prop"]=> int(5) } [1]=> object(stdClass)#%d (1) { ["prop"]=> int(5) } } SAME array(2) { [0]=> &object(stdClass)#%d (1) { ["prop"]=> int(6) } [1]=> &object(stdClass)#%d (1) { ["prop"]=> int(6) } } SAME igbinary-3.2.13/tests/igbinary_046.phpt0000664000175000017500000000074214366750307016772 0ustar tysontyson--TEST-- Correctly unserialize scalar refs. --INI-- igbinary.compact_strings = On --FILE-- &string(1) "V" [1]=> &string(1) "V" [2]=> &string(1) "V" [3]=> &string(1) "V" } igbinary-3.2.13/tests/igbinary_046b.phpt0000664000175000017500000000130114366750307017124 0ustar tysontyson--TEST-- Correctly unserialize multiple object refs. --SKIPIF-- --INI-- igbinary.compact_strings = On --FILE-- &string(1) "V" [1]=> &string(1) "V" [2]=> &string(1) "V" [3]=> &string(1) "V" } igbinary-3.2.13/tests/igbinary_046c.phpt0000664000175000017500000000121314366750307017127 0ustar tysontyson--TEST-- Correctly unserialize multiple array refs. --SKIPIF-- --INI-- igbinary.compact_strings = On --FILE-- &string(1) "V" [1]=> &string(1) "V" [2]=> &string(1) "V" [3]=> &string(1) "V" } igbinary-3.2.13/tests/igbinary_046d.phpt0000664000175000017500000000234614366750307017140 0ustar tysontyson--TEST-- Correctly unserialize multiple object refs and non-refs. --INI-- igbinary.compact_strings = On --FILE-- object(stdClass)#%d (0) { } [1]=> &object(stdClass)#%d (0) { } [2]=> &object(stdClass)#%d (0) { } [3]=> object(stdClass)#%d (0) { } } a:4:{i:0;O:8:"stdClass":0:{}i:1;R:2;i:2;R:2;i:3;r:2;} 00000002140406001708737464436c61737314000601252201060225220106032201 a:4:{i:0;O:8:"stdClass":0:{}i:1;R:2;i:2;R:2;i:3;r:2;} array(4) { [0]=> object(stdClass)#%d (0) { } [1]=> &object(stdClass)#%d (0) { } [2]=> &object(stdClass)#%d (0) { } [3]=> object(stdClass)#%d (0) { } } array(4) { [0]=> object(stdClass)#%d (0) { } [1]=> &string(1) "V" [2]=> &string(1) "V" [3]=> object(stdClass)#%d (0) { } } igbinary-3.2.13/tests/igbinary_047.phpt0000664000175000017500000000404514366750307016773 0ustar tysontyson--TEST-- Check for serialization handler, SessionHandlerInterface --SKIPIF-- --FILE-- Returns the number of deleted sessions on success, or false on failure. // > Note this value is returned internally to PHP for processing. public function gc($time): int { return 0; } } class Foo { } class Bar { } ini_set('session.serialize_handler', 'igbinary'); $handler = new S(); session_set_save_handler($handler, true); $db_object = new Foo(); $session_object = new Bar(); $v = session_start(); var_dump($v); $_SESSION['test'] = "foobar"; session_write_close(); echo $output; /* * you can add regression tests for your extension here * * the output of your test code has to be equal to the * text in the --EXPECT-- section below for the tests * to pass, differences between the output and the * expected text are interpreted as failure * * see TESTING.md for further information on * writing regression tests */ ?> --EXPECT-- bool(true) read wrote: 14021103666f6f06011104746573741106666f6f626172 igbinary-3.2.13/tests/igbinary_048.phpt0000664000175000017500000000105014366750307016765 0ustar tysontyson--TEST-- Object test, __set not called for private attr in extended class --FILE-- a = [1, 2, 3]; $x->nonexistent = 'aaa'; igbinary_unserialize(igbinary_serialize($x)); --EXPECT-- magic function called for nonexistent with 'aaa' igbinary-3.2.13/tests/igbinary_048b.phpt0000664000175000017500000000102614366750307017132 0ustar tysontyson--TEST-- Object test, __set not called for private attr in extended class --FILE-- a = [1, 2, 3]; $x->nonexistent = 'aaa'; unserialize(serialize($x)); --EXPECT-- magic function called for nonexistent with 'aaa' igbinary-3.2.13/tests/igbinary_049.phpt0000664000175000017500000000346614366750307017003 0ustar tysontyson--TEST-- Correctly unserialize multiple references in arrays --SKIPIF-- --INI-- igbinary.compact_strings = On --FILE-- prop = &$a[8]; $a[11] = &$a[10]; $a[12] = $a[9]; $ig_ser = igbinary_serialize($a); printf("ig_ser=%s\n", bin2hex($ig_ser)); $ig = igbinary_unserialize($ig_ser); var_dump($ig); $f = &$ig[3]; $f = 'V'; $g = &$ig[5]; $g = 'H'; $h = $ig[10]; $h->prop = 'S'; var_dump($ig); --EXPECTF-- ig_ser=00000002140d0600251101410601250101060225010106032501010604250406052501020606251703466f6f1400060725220306082522030609140106000621060a251708737464436c6173731401110470726f70252203060b252205060c0104 array(13) { [0]=> &string(1) "A" [1]=> &string(1) "A" [2]=> &string(1) "A" [3]=> &string(1) "A" [4]=> &bool(false) [5]=> &bool(false) [6]=> &object(Foo)#%d (0) { } [7]=> &object(Foo)#%d (0) { } [8]=> &object(Foo)#%d (0) { } [9]=> array(1) { [0]=> int(33) } [10]=> &object(stdClass)#%d (1) { ["prop"]=> &object(Foo)#%d (0) { } } [11]=> &object(stdClass)#%d (1) { ["prop"]=> &object(Foo)#%d (0) { } } [12]=> array(1) { [0]=> int(33) } } array(13) { [0]=> &string(1) "V" [1]=> &string(1) "V" [2]=> &string(1) "V" [3]=> &string(1) "V" [4]=> &string(1) "H" [5]=> &string(1) "H" [6]=> &string(1) "S" [7]=> &string(1) "S" [8]=> &string(1) "S" [9]=> array(1) { [0]=> int(33) } [10]=> &object(stdClass)#%d (1) { ["prop"]=> &string(1) "S" } [11]=> &object(stdClass)#%d (1) { ["prop"]=> &string(1) "S" } [12]=> array(1) { [0]=> int(33) } } igbinary-3.2.13/tests/igbinary_049b.phpt0000664000175000017500000000264714366750307017145 0ustar tysontyson--TEST-- Correctly unserialize multiple references in objects --SKIPIF-- --INI-- igbinary.compact_strings = On --FILE-- x0 = NULL; $a->x1 = &$a->x0; $a->x2 = &$a->x1; $a->x3 = &$a->x2; $a->x4 = false; $a->x5 = &$a->x4; $a->x6 = new Foo(); $a->x7 = &$a->x6; $a->x8 = &$a->x7; $a->x9 = array(33); $a->x10 = new stdClass(); $a->x10->prop = &$a->x8; $a->x11 = &$a->x10; $a->x12 = $a->x9; $ig_ser = igbinary_serialize($a); printf("ig_ser=%s\n", bin2hex($ig_ser)); $ig = igbinary_unserialize($ig_ser); $f = &$ig->x3; $f = 'V'; $g = &$ig->x5; $g = 'H'; $h = $ig->x10; $h->prop = 'S'; var_dump($ig); --EXPECTF-- ig_ser=000000021708737464436c617373140d1102783025001102783125010111027832250101110278332501011102783425041102783525010211027836251703466f6f14001102783725220311027838252203110278391401060006211103783130251a001401110470726f70252203110378313125220511037831320104 object(stdClass)#%d (13) { ["x0"]=> &string(1) "V" ["x1"]=> &string(1) "V" ["x2"]=> &string(1) "V" ["x3"]=> &string(1) "V" ["x4"]=> &string(1) "H" ["x5"]=> &string(1) "H" ["x6"]=> &string(1) "S" ["x7"]=> &string(1) "S" ["x8"]=> &string(1) "S" ["x9"]=> array(1) { [0]=> int(33) } ["x10"]=> &object(stdClass)#%d (1) { ["prop"]=> &string(1) "S" } ["x11"]=> &object(stdClass)#%d (1) { ["prop"]=> &string(1) "S" } ["x12"]=> array(1) { [0]=> int(33) } } igbinary-3.2.13/tests/igbinary_050.phpt0000664000175000017500000000202114366750307016755 0ustar tysontyson--TEST-- Correctly unserialize cyclic object references --SKIPIF-- --INI-- igbinary.compact_strings = On --FILE-- foo = &$a; $a->bar = &$a; $b = new stdClass(); $b->cyclic = &$a; printf("%s\n", serialize($b)); $ig_ser = igbinary_serialize($b); printf("%s\n", bin2hex($ig_ser)); $ig = igbinary_unserialize($ig_ser); printf("%s\n", serialize($ig)); var_dump($ig); $f = &$ig->cyclic->foo; $f = 'V'; var_dump($ig); // Note: While the php7 unserializer consistently makes a distinction between refs to an object and non-refs, // the php5 serializer does not. --EXPECTF-- O:8:"stdClass":1:{s:6:"cyclic";O:8:"stdClass":2:{s:3:"foo";R:2;s:3:"bar";R:2;}} 000000021708737464436c617373140111066379636c6963251a0014021103666f6f2522011103626172252201 O:8:"stdClass":1:{s:6:"cyclic";O:8:"stdClass":2:{s:3:"foo";R:2;s:3:"bar";R:2;}} object(stdClass)#3 (1) { ["cyclic"]=> &object(stdClass)#4 (2) { ["foo"]=> *RECURSION* ["bar"]=> *RECURSION* } } object(stdClass)#3 (1) { ["cyclic"]=> &string(1) "V" } igbinary-3.2.13/tests/igbinary_051.phpt0000664000175000017500000000145714366750307016772 0ustar tysontyson--TEST-- Object test, __wakeup (With multiple references) --SKIPIF-- --FILE-- a = $a; $this->b = $b; } function __wakeup() { $this->b = $this->a * 3; } } function main() { $o = new Obj(1, 2); $variable = array(&$o, &$o); $serialized = igbinary_serialize($variable); $unserialized = igbinary_unserialize($serialized); echo substr(bin2hex($serialized), 8), "\n"; echo $unserialized[0]->b === 3 && $unserialized[0]->a === 1 ? 'OK' : 'ERROR'; echo "\n"; $unserialized[0] = 'a'; var_dump($unserialized); } main(); --EXPECT-- 140206002517034f626a1402110161060111016206020601252201 OK array(2) { [0]=> &string(1) "a" [1]=> &string(1) "a" } igbinary-3.2.13/tests/igbinary_052.phpt0000664000175000017500000000325614366750307016772 0ustar tysontyson--TEST-- Object Serializable interface can be serialized in references --INI-- ; Note that php 8.1 deprecates using Serializable without __serialize/__unserialize but we are testing Serialize for igbinary. Suppress deprecations. error_reporting=E_ALL & ~E_DEPRECATED --FILE-- a = $a; $this->b = $b; } public function serialize() { $c = self::$count++; echo "call serialize\n"; return pack('NN', $this->a, $this->b); } public function unserialize($serialized) { $tmp = unpack('N*', $serialized); $this->__construct($tmp[1], $tmp[2]); $c = self::$count++; echo "call unserialize\n"; } } function main() { $a = new Obj(1, 0); $b = new Obj(42, 43); $variable = array(&$a, &$a, $b); $serialized = igbinary_serialize($variable); printf("%s\n", bin2hex($serialized)); $unserialized = igbinary_unserialize($serialized); var_dump($unserialized); $unserialized[0] = 'A'; var_dump($unserialized[1]); } main(); --EXPECTF-- call serialize call serialize 00000002140306002517034f626a1d080000000100000000060125220106021a001d080000002a0000002b call unserialize call unserialize array(3) { [0]=> &object(Obj)#%d (2) { ["a"]=> int(1) ["b"]=> int(0) } [1]=> &object(Obj)#%d (2) { ["a"]=> int(1) ["b"]=> int(0) } [2]=> object(Obj)#%d (2) { ["a"]=> int(42) ["b"]=> int(43) } } string(1) "A" igbinary-3.2.13/tests/igbinary_053.phpt0000664000175000017500000000176414366750307016775 0ustar tysontyson--TEST-- __wakeup can modify properties without affecting other objects --FILE-- a = $a; } public function __wakeup() { echo "call wakeup\n"; $this->a[] = "end"; } } function main() { $array = ["test"]; // array (not a reference, but should be copied on write) $a = new Obj($array); $b = new Obj($array); $variable = [$a, $b]; $serialized = igbinary_serialize($variable); printf("%s\n", bin2hex($serialized)); $unserialized = igbinary_unserialize($serialized); var_dump($unserialized); } main(); --EXPECTF-- 000000021402060017034f626a14011101611401060011047465737406011a0014010e010102 call wakeup call wakeup array(2) { [0]=> object(Obj)#%d (1) { ["a"]=> array(2) { [0]=> string(4) "test" [1]=> string(3) "end" } } [1]=> object(Obj)#%d (1) { ["a"]=> array(2) { [0]=> string(4) "test" [1]=> string(3) "end" } } } igbinary-3.2.13/tests/igbinary_054.phpt0000664000175000017500000000262714366750307016775 0ustar tysontyson--TEST-- __wakeup can add dynamic properties without affecting other objects --FILE-- a being a dynamic property. function __construct($a) { $this->a = $a; } public function __wakeup() { echo "Calling __wakeup\n"; for ($i = 0; $i < 10000; $i++) { $this->{'b' . $i} = 42; } } } function main() { $array = array("roh"); // array (not a reference, but should be copied on write) $a = new Obj($array); $b = new Obj($array); $c = new Obj(null); $variable = array($a, $b, $c); $serialized = igbinary_serialize($variable); printf("%s\n", bin2hex($serialized)); $unserialized = igbinary_unserialize($serialized); echo "Called igbinary_unserialize\n"; for ($a = 0; $a < 3; $a++) { for ($i = 0; $i < 10000; $i++) { if ($unserialized[$a]->{'b' . $i} !== 42) { echo "Fail $a b$i\n"; return; } unset($unserialized[$a]->{'b' . $i}); } } var_dump($unserialized); } main(); --EXPECTF-- 000000021403060017034f626a1401110161140106001103726f6806011a0014010e01010206021a0014010e0100 Calling __wakeup Calling __wakeup Calling __wakeup Called igbinary_unserialize array(3) { [0]=> object(Obj)#%d (1) { ["a"]=> array(1) { [0]=> string(3) "roh" } } [1]=> object(Obj)#%d (1) { ["a"]=> array(1) { [0]=> string(3) "roh" } } [2]=> object(Obj)#%d (1) { ["a"]=> NULL } } igbinary-3.2.13/tests/igbinary_055.phpt0000664000175000017500000000123514366750307016770 0ustar tysontyson--TEST-- __wakeup can replace a copy of the object referring to the root node. --FILE-- a = $a; } public function __wakeup() { echo "Calling __wakeup\n"; $this->a = "replaced"; } } $a = new stdClass(); $a->obj = new Obj($a);; $serialized = igbinary_serialize($a); printf("%s\n", bin2hex($serialized)); $unserialized = igbinary_unserialize($serialized); var_dump($unserialized); --EXPECTF-- 000000021708737464436c617373140111036f626a17034f626a14011101612200 Calling __wakeup object(stdClass)#%d (1) { ["obj"]=> object(Obj)#%d (1) { ["a"]=> string(8) "replaced" } } igbinary-3.2.13/tests/igbinary_057.phpt0000664000175000017500000000174714366750307017002 0ustar tysontyson--TEST-- Test serializing more strings than the capacity of the initial strings table. --SKIPIF-- --FILE-- --EXPECT-- 1432060011016106011101620602110163060311016406041101650605110166060611016706071101680608110169060911016a060a11016b060b11016c060c11016d060d11016e060e11016f060f11017006101101710611110172061211017306131101740614110175061511017606161101770617110178061811017906190e00061a0e01061b0e02061c0e03061d0e04061e0e05061f0e0606200e0706210e0806220e0906230e0a06240e0b06250e0c06260e0d06270e0e06280e0f06290e10062a0e11062b0e12062c0e13062d0e14062e0e15062f0e1606300e1706310e18 a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y igbinary-3.2.13/tests/igbinary_058.phpt0000664000175000017500000000255514366750307017001 0ustar tysontyson--TEST-- Should not call __destruct if __wakeup throws an exception --INI-- igbinary.compact_strings = On --FILE-- id = $id; $this->throws = $throws; $this->dynamic = "original"; } public function __wakeup() { printf("Calling __wakeup %s\n", $this->id); $this->dynamic = "copy"; if ($this->throws) { throw new Exception("__wakeup threw"); } } public function __destruct() { printf("Calling __destruct %s dynamic=%s\n", $this->id, $this->dynamic); } } function main() { $a = new Thrower("a", true); $serialized = igbinary_serialize($a); try { igbinary_unserialize($serialized); } catch (Exception $e) { printf("Caught %s\n", $e->getMessage()); } $a = null; print("Done a\n"); $b = new Thrower("b", false); $serialized = igbinary_serialize($b); $bCopy = igbinary_unserialize($serialized); print("Unserialized b\n"); var_dump($bCopy); $bCopy = null; $b = null; print("Done b\n"); } main(); --EXPECT-- Calling __wakeup a Caught __wakeup threw Calling __destruct a dynamic=original Done a Calling __wakeup b Unserialized b object(Thrower)#2 (3) { ["id"]=> string(1) "b" ["throws"]=> bool(false) ["dynamic"]=> string(4) "copy" } Calling __destruct b dynamic=copy Calling __destruct b dynamic=original Done b igbinary-3.2.13/tests/igbinary_058b.phpt0000664000175000017500000000257514366750307017145 0ustar tysontyson--TEST-- Should not call __destruct if __wakeup throws an exception (in arrays) --INI-- igbinary.compact_strings = On --FILE-- id = $id; $this->throws = $throws; $this->dynamic = "original"; } public function __wakeup() { printf("Calling __wakeup %s\n", $this->id); $this->dynamic = "copy"; if ($this->throws) { throw new Exception("__wakeup threw for id " . $this->id); } } public function __destruct() { printf("Calling __destruct %s dynamic=%s\n", $this->id, $this->dynamic); } } function main() { $values = [ 0 => new Thrower("a", false), 'foo' => 'last', 'key1' => new Thrower("b", false), 2 => new Thrower("c", true), 'last' => new Thrower("d", false), ]; $serialized = igbinary_serialize($values); $values = null; printf("Going to unserialize\n"); try { igbinary_unserialize($serialized); } catch (Exception $e) { printf("Caught %s\n", $e->getMessage()); } } main(); --EXPECT-- Calling __destruct a dynamic=original Calling __destruct b dynamic=original Calling __destruct c dynamic=original Calling __destruct d dynamic=original Going to unserialize Calling __wakeup a Calling __wakeup b Calling __wakeup c Calling __destruct a dynamic=copy Calling __destruct b dynamic=copy Caught __wakeup threw for id c igbinary-3.2.13/tests/igbinary_059.phpt0000664000175000017500000000236514366750307017001 0ustar tysontyson--TEST-- igbinary_unserialize should never convert from packed array to hash when references exist (Bug #48) --SKIPIF-- --FILE-- = 16) is added (converted to a hash), causing a segfault. foreach (array(0, 50) as $i) { $inner = new stdClass(); $inner->a = $i; $result[0][0][$i] = $inner; $result[1][] = $inner; } $serialized = igbinary_serialize($result); printf("%s\n", bin2hex(substr($serialized, 4))); flush(); $unserialized = igbinary_unserialize($serialized); var_dump($unserialized); } main(); ?> --EXPECTF-- 1402060014010600140206001708737464436c6173731401110161060006321a0014010e010632060114020600220306012204 array(2) { [0]=> array(1) { [0]=> array(2) { [0]=> object(stdClass)#%d (1) { ["a"]=> int(0) } [50]=> object(stdClass)#%d (1) { ["a"]=> int(50) } } } [1]=> array(2) { [0]=> object(stdClass)#%d (1) { ["a"]=> int(0) } [1]=> object(stdClass)#%d (1) { ["a"]=> int(50) } } } igbinary-3.2.13/tests/igbinary_060.phpt0000664000175000017500000000251714366750307016770 0ustar tysontyson--TEST-- Igbinary_unserialize_header warning --SKIPIF-- --FILE-- c = $this->b . 'OnWakeup'; } } error_reporting(E_ALL); // Start session session_start(); // The serialization of DateTime varies in php5 and php 7. $_SESSION['date'] = new \DateTime('@1234567890'); $a = new A(); $a->b = 'value'; $_SESSION['a'] = $a; var_dump($_SESSION['date']->getTimestamp()); $mydata = session_encode(); unset($_SESSION['date']); var_dump(session_decode($mydata)); var_dump($_SESSION['date']->getTimestamp()); var_dump($_SESSION['a']->c); ?> --EXPECT-- int(1234567890) bool(true) int(1234567890) string(13) "valueOnWakeup" igbinary-3.2.13/tests/igbinary_062.phpt0000664000175000017500000000233714366750307016772 0ustar tysontyson--TEST-- igbinary should not call __wakeup() if Serializable::unserialize was used to unserialize the object data (like `unserialize`) --INI-- ; Note that php 8.1 deprecates using Serializable without __serialize/__unserialize but we are testing Serialize for igbinary. Suppress deprecations. error_reporting=E_ALL & ~E_DEPRECATED --FILE-- prop . "\n"; return $this->prop; } public function unserialize($data) { echo "In unserialize $data\n"; $this->prop = $data; } public function __wakeup() { echo "In __wakeup, unexpectedly\n"; } } function testA() { $a = new A(); $a->prop = 'other'; $ser = serialize($a); $b = unserialize($ser); var_dump($b); $c = new A(); $c->prop = 'igprop'; $serC = igbinary_serialize($c); echo bin2hex($serC) . "\n"; $d = igbinary_unserialize($serC); var_dump($d); } testA(); ?> --EXPECTF-- In serialize other In unserialize other object(A)#%d (1) { ["prop"]=> string(5) "other" } In serialize igprop 000000021701411d06696770726f70 In unserialize igprop object(A)#%d (1) { ["prop"]=> string(6) "igprop" } igbinary-3.2.13/tests/igbinary_063_php7.phpt0000664000175000017500000000231214366750307017722 0ustar tysontyson--TEST-- Accessing unserialized numbers. --SKIPIF-- = 70200) { echo "Skip php 7.1 or 7.0 required"; } ?> --FILE-- 'x', 1234 => 33); var_dump($data); $x = "1"; $y = 1; $z = "1234"; $w = 1234; var_dump(isset($data->{$x}) ? $data->{$x} : "unset"); error_reporting(0); $str = igbinary_serialize($data); $unserialized = igbinary_unserialize($str); var_dump($unserialized); var_dump(isset($unserialized->{$x}) ? $unserialized->{$x} : "unset str"); var_dump(isset($unserialized->{$y}) ? $unserialized->{$y} : "unset int"); var_dump(isset($unserialized->{$z}) ? $unserialized->{$z} : "unset str 1234"); var_dump(isset($unserialized->{$w}) ? $unserialized->{$w} : "unset int 1234"); var_dump(isset($unserialized->{-1}) ? $unserialized->{-1} : "unset int -1"); ?> --EXPECT-- object(stdClass)#1 (5) { [0]=> int(1) [1]=> int(2) [2]=> int(3) [-1]=> string(1) "x" [1234]=> int(33) } string(5) "unset" object(stdClass)#2 (5) { ["0"]=> int(1) ["1"]=> int(2) ["2"]=> int(3) ["-1"]=> string(1) "x" ["1234"]=> int(33) } int(2) int(2) int(33) int(33) string(1) "x" igbinary-3.2.13/tests/igbinary_063_php72.phpt0000664000175000017500000000227614366750307020015 0ustar tysontyson--TEST-- Accessing unserialized numbers. --SKIPIF-- --FILE-- 'x', 1234 => 33); var_dump($data); $x = "1"; $y = 1; $z = "1234"; $w = 1234; var_dump(isset($data->{$x}) ? $data->{$x} : "unset"); error_reporting(0); $str = igbinary_serialize($data); $unserialized = igbinary_unserialize($str); var_dump($unserialized); var_dump(isset($unserialized->{$x}) ? $unserialized->{$x} : "unset str"); var_dump(isset($unserialized->{$y}) ? $unserialized->{$y} : "unset int"); var_dump(isset($unserialized->{$z}) ? $unserialized->{$z} : "unset str 1234"); var_dump(isset($unserialized->{$w}) ? $unserialized->{$w} : "unset int 1234"); var_dump(isset($unserialized->{-1}) ? $unserialized->{-1} : "unset int -1"); ?> --EXPECT-- object(stdClass)#1 (5) { ["0"]=> int(1) ["1"]=> int(2) ["2"]=> int(3) ["-1"]=> string(1) "x" ["1234"]=> int(33) } int(2) object(stdClass)#2 (5) { ["0"]=> int(1) ["1"]=> int(2) ["2"]=> int(3) ["-1"]=> string(1) "x" ["1234"]=> int(33) } int(2) int(2) int(33) int(33) string(1) "x" igbinary-3.2.13/tests/igbinary_064.phpt0000664000175000017500000000427514366750307016777 0ustar tysontyson--TEST-- Works when there are hash collisions in strings when serializing. --SKIPIF-- --FILE-- x = $x; } } class Ez { public $FyEz = 'EzEz'; } class G8 { public $FyG8; } $data = array(new Fy('G8G8'), new Fy('EzG8'), new Ez(), new G8(), new Ez(), 'G8' => new G8(), 'F8Ez' => new G8(), array(new G8())); var_dump($data); echo "\n"; $str = igbinary_serialize($data); echo bin2hex($str) . "\n"; $unserialized = igbinary_unserialize($str); var_dump($unserialized); echo "\n"; var_export(serialize($data) === serialize($unserialized)); ?> --EXPECT-- array(8) { [0]=> object(Fy)#1 (2) { ["EzFy"]=> int(2) ["x"]=> string(4) "G8G8" } [1]=> object(Fy)#2 (2) { ["EzFy"]=> int(2) ["x"]=> string(4) "EzG8" } [2]=> object(Ez)#3 (1) { ["FyEz"]=> string(4) "EzEz" } [3]=> object(G8)#4 (1) { ["FyG8"]=> NULL } [4]=> object(Ez)#5 (1) { ["FyEz"]=> string(4) "EzEz" } ["G8"]=> object(G8)#6 (1) { ["FyG8"]=> NULL } ["F8Ez"]=> object(G8)#7 (1) { ["FyG8"]=> NULL } [5]=> array(1) { [0]=> object(G8)#8 (1) { ["FyG8"]=> NULL } } } 00000002140806001702467914021104457a4679060211017811044738473806011a0014020e0106020e021104457a473806021702457a140111044679457a1104457a457a06031702473814011104467947380006041a0514010e060e070e081a0814010e090011044638457a1a0814010e09000605140106001a0814010e0900 array(8) { [0]=> object(Fy)#9 (2) { ["EzFy"]=> int(2) ["x"]=> string(4) "G8G8" } [1]=> object(Fy)#10 (2) { ["EzFy"]=> int(2) ["x"]=> string(4) "EzG8" } [2]=> object(Ez)#11 (1) { ["FyEz"]=> string(4) "EzEz" } [3]=> object(G8)#12 (1) { ["FyG8"]=> NULL } [4]=> object(Ez)#13 (1) { ["FyEz"]=> string(4) "EzEz" } ["G8"]=> object(G8)#14 (1) { ["FyG8"]=> NULL } ["F8Ez"]=> object(G8)#15 (1) { ["FyG8"]=> NULL } [5]=> array(1) { [0]=> object(G8)#16 (1) { ["FyG8"]=> NULL } } } true igbinary-3.2.13/tests/igbinary_065.phpt0000664000175000017500000000303414366750307016770 0ustar tysontyson--TEST-- Don't emit zval has unknown type 0 (IS_UNDEF) --FILE-- x); unset($this->y); unset($this->z); $this->set = 'setVal'; $this->priv = null; $this->omitted = 'otherVal'; return ['kept', 'x', 'y', 'z', 'set', 'priv']; } } error_reporting(E_ALL); // TODO: emit 'Notice: igbinary_serialize(): "x" returned as member variable from __sleep() but does not exist' instead. $serialized = igbinary_serialize(new MyClass()); echo bin2hex($serialized) . "\n"; var_export_normalized(igbinary_unserialize($serialized)); echo "\n"; ?> --EXPECTF-- Notice: igbinary_serialize(): "x" returned as member variable from __sleep() but does not exist in %s on line 25 Notice: igbinary_serialize(): "y" returned as member variable from __sleep() but does not exist in %s on line 25 Notice: igbinary_serialize(): "z" returned as member variable from __sleep() but does not exist in %s on line 25 0000000217074d79436c617373140611046b6570740602110178001104002a007900110a004d79436c617373007a001106002a00736574110673657456616c110d004d79436c617373007072697600 MyClass::__set_state(array( 'kept' => 2, 'x' => NULL, 'y' => NULL, 'z' => NULL, 'set' => 'setVal', 'priv' => NULL, 'omitted' => 'myVal', )) igbinary-3.2.13/tests/igbinary_066.phpt0000664000175000017500000000217114366750307016772 0ustar tysontyson--TEST-- Test serializing different empty arrays --FILE-- array(), '2nd' => array(), '3rd' => array())))); echo "\n"; var_dump(igbinary_unserialize(igbinary_serialize(array('1st' => $a, '2nd' => $a, '3rd' => $a)))); echo "\n"; $result = igbinary_unserialize(igbinary_serialize(array('1st' => $a, '2nd' => &$a, '3rd' => &$a, '4th' => array()))); var_dump($result); $result['2nd'][] = 2; var_dump($result); echo "\n"; ?> --EXPECT-- array(3) { ["1st"]=> array(0) { } ["2nd"]=> array(0) { } ["3rd"]=> array(0) { } } array(3) { ["1st"]=> array(0) { } ["2nd"]=> array(0) { } ["3rd"]=> array(0) { } } array(4) { ["1st"]=> array(0) { } ["2nd"]=> &array(0) { } ["3rd"]=> &array(0) { } ["4th"]=> array(0) { } } array(4) { ["1st"]=> array(0) { } ["2nd"]=> &array(1) { [0]=> int(2) } ["3rd"]=> &array(1) { [0]=> int(2) } ["4th"]=> array(0) { } } igbinary-3.2.13/tests/igbinary_067.phpt0000664000175000017500000000146614366750307017001 0ustar tysontyson--TEST-- Test serializing multiple reference groups to the same empty array --SKIPIF-- --FILE-- $value) { echo "$k: " . json_encode($value) . "\n"; } } function main() { $a = array(); $b = $a; $c = $a; $value = array(&$b, $a, &$b, &$c, &$c); $ser = igbinary_serialize($value); echo bin2hex($ser) . "\n"; $v = igbinary_unserialize($ser); dump($v); $v[0][] = 2; dump($v); $v[3][] = 3; dump($v); var_export($a); } main(); ?> --EXPECT-- 000000021405060025140006011400060225010106032514000604250103 5 values 0: [] 1: [] 2: [] 3: [] 4: [] 5 values 0: [2] 1: [] 2: [2] 3: [] 4: [] 5 values 0: [2] 1: [] 2: [2] 3: [3] 4: [3] array ( ) igbinary-3.2.13/tests/igbinary_068.phpt0000664000175000017500000000071514366750307016776 0ustar tysontyson--TEST-- Test serializing and unserializing PHP_INT_MIN --FILE-- --EXPECT-- true true true igbinary-3.2.13/tests/igbinary_069.phpt0000664000175000017500000000054714366750307017002 0ustar tysontyson--TEST-- Test serializing and unserializing many duplicate strings --FILE-- --FILE-- $this->prop, 42 => $this->prop2]; } public function __unserialize(array $data) { $this->prop = 'unser' . $data["value"]; $this->prop2 = 'unser' . $data[42]; } } $test = new Test; $test->prop = "foobar"; $test->prop2 = "barfoo"; $s = igbinary_serialize($test); echo bin2hex($s) . "\n"; var_dump(igbinary_unserialize($s)); ?> --EXPECT-- 000000021704546573741402110576616c75651106666f6f626172062a1106626172666f6f object(Test)#2 (2) { ["prop"]=> string(11) "unserfoobar" ["prop2"]=> string(11) "unserbarfoo" } igbinary-3.2.13/tests/igbinary_071.phpt0000664000175000017500000000466314366750307016776 0ustar tysontyson--TEST-- igbinary_unserialize with references to typed properties shall skip the references or fail --SKIPIF-- = 80000) { echo "skip different error message format"; } ?> --FILE-- a = 1234; $a->b = &$a->a; var_dump(bin2hex($s = igbinary_serialize($a))); var_dump(igbinary_unserialize($s)); echo "Test B\n"; $b = new B(); $b->a = -1234; $b->b = &$b->a; var_dump(bin2hex($s = igbinary_serialize($b))); var_dump(igbinary_unserialize($s)); $z = new Z(); $z->a = null; $z->b = &$z->a; $s = igbinary_serialize($z); try { var_dump(igbinary_unserialize(str_replace('Z', 'A', $s))); } catch (TypeError $e) { echo $e->getMessage(), "\n"; } try { var_dump(igbinary_unserialize(str_replace('Z', 'B', $s))); } catch (TypeError $e) { echo $e->getMessage(), "\n"; } $z = new Z(); $z->a = 1; $z->b = &$z->a; $s = igbinary_serialize($z); try { var_dump(igbinary_unserialize(str_replace('Z', 'C', $s))); } catch (TypeError $e) { echo $e->getMessage(), "\n"; } $z = new Z(); $z->a = 'x'; $z->b = &$z->a; $s = igbinary_serialize($z); try { var_dump(igbinary_unserialize(str_replace('Z', 'C', $s))); } catch (TypeError $e) { echo $e->getMessage(), "\n"; } $z = new Z(); $z->a = 1; $z->b = &$z->a; $s = igbinary_serialize($z); try { var_dump(igbinary_unserialize(str_replace('Z', 'D', $s))); } catch (TypeError $e) { echo $e->getMessage(), "\n"; } /* try { var_dump(unserialize('O:1:"D":2:{s:1:"a";i:1;s:1:"b";R:2;}')); } catch (TypeError $e) { echo $e->getMessage(), "\n"; } */ ?> --EXPECT-- string(44) "000000021701411402110161250804d2110162250101" object(A)#2 (2) { ["a"]=> &int(1234) ["b"]=> &int(1234) } Test B string(44) "000000021701421402110161250904d2110162250101" object(B)#3 (2) { ["a"]=> &int(-1234) ["b"]=> &int(-1234) } Typed property A::$a must be int, null used Typed property B::$b must be int, null used Typed property C::$b must be string, int used Typed property C::$a must be int, string used Reference with value of type int held by property D::$a of type int is not compatible with property D::$b of type float igbinary-3.2.13/tests/igbinary_071_php8.phpt0000664000175000017500000000453614366750307017734 0ustar tysontyson--TEST-- igbinary_unserialize with references to typed properties shall skip the references or fail --SKIPIF-- --FILE-- a = 1234; $a->b = &$a->a; var_dump(bin2hex($s = igbinary_serialize($a))); var_dump(igbinary_unserialize($s)); echo "Test B\n"; $b = new B(); $b->a = -1234; $b->b = &$b->a; var_dump(bin2hex($s = igbinary_serialize($b))); var_dump(igbinary_unserialize($s)); $z = new Z(); $z->a = null; $z->b = &$z->a; $s = igbinary_serialize($z); try { var_dump(igbinary_unserialize(str_replace('Z', 'A', $s))); } catch (TypeError $e) { echo $e->getMessage(), "\n"; } try { var_dump(igbinary_unserialize(str_replace('Z', 'B', $s))); } catch (TypeError $e) { echo $e->getMessage(), "\n"; } $z = new Z(); $z->a = 1; $z->b = &$z->a; $s = igbinary_serialize($z); try { var_dump(igbinary_unserialize(str_replace('Z', 'C', $s))); } catch (TypeError $e) { echo $e->getMessage(), "\n"; } $z = new Z(); $z->a = 'x'; $z->b = &$z->a; $s = igbinary_serialize($z); try { var_dump(igbinary_unserialize(str_replace('Z', 'C', $s))); } catch (TypeError $e) { echo $e->getMessage(), "\n"; } $z = new Z(); $z->a = 1; $z->b = &$z->a; $s = igbinary_serialize($z); try { var_dump(igbinary_unserialize(str_replace('Z', 'D', $s))); } catch (TypeError $e) { echo $e->getMessage(), "\n"; } /* try { var_dump(unserialize('O:1:"D":2:{s:1:"a";i:1;s:1:"b";R:2;}')); } catch (TypeError $e) { echo $e->getMessage(), "\n"; } */ ?> --EXPECT-- string(44) "000000021701411402110161250804d2110162250101" object(A)#2 (2) { ["a"]=> &int(1234) ["b"]=> &int(1234) } Test B string(44) "000000021701421402110161250904d2110162250101" object(B)#3 (2) { ["a"]=> &int(-1234) ["b"]=> &int(-1234) } Cannot assign null to property A::$a of type int Cannot assign null to property B::$b of type int Cannot assign int to property C::$b of type string Cannot assign string to property C::$a of type int Reference with value of type int held by property D::$a of type int is not compatible with property D::$b of type floatigbinary-3.2.13/tests/igbinary_072.phpt0000664000175000017500000000300614366750307016765 0ustar tysontyson--TEST-- igbinary and __PHP_INCOMPLETE_CLASS --FILE-- = 80200) { require_once __DIR__ . '/php82_suppress_dynamic_properties_warning.inc'; } class Test {} function test_ser_unser($obj) { var_dump(bin2hex($s = igbinary_serialize($obj))); $s = str_replace('Test', 'Best', $s); $obj2 = igbinary_unserialize($s); var_dump($obj2); var_dump(bin2hex($s = igbinary_serialize($obj2))); var_dump(igbinary_unserialize($s)); } test_ser_unser(new Test()); echo "Testing with properties\n"; $obj = new Test(); $obj->dynamicProp = 'value'; $obj->nullProp = null; test_ser_unser($obj); ?> --EXPECT-- string(24) "000000021704546573741400" object(__PHP_Incomplete_Class)#2 (1) { ["__PHP_Incomplete_Class_Name"]=> string(4) "Best" } string(24) "000000021704426573741400" object(__PHP_Incomplete_Class)#3 (1) { ["__PHP_Incomplete_Class_Name"]=> string(4) "Best" } Testing with properties string(86) "000000021704546573741402110b64796e616d696350726f70110576616c756511086e756c6c50726f7000" object(__PHP_Incomplete_Class)#1 (3) { ["__PHP_Incomplete_Class_Name"]=> string(4) "Best" ["dynamicProp"]=> string(5) "value" ["nullProp"]=> NULL } string(86) "000000021704426573741402110b64796e616d696350726f70110576616c756511086e756c6c50726f7000" object(__PHP_Incomplete_Class)#3 (3) { ["__PHP_Incomplete_Class_Name"]=> string(4) "Best" ["dynamicProp"]=> string(5) "value" ["nullProp"]=> NULL }igbinary-3.2.13/tests/igbinary_073.phpt0000664000175000017500000000325614366750307016775 0ustar tysontyson--TEST-- igbinary and large Serializable --INI-- ; Note that php 8.1 deprecates using Serializable without __serialize/__unserialize but we are testing Serialize for igbinary. Suppress deprecations. error_reporting=E_ALL & ~E_DEPRECATED --FILE-- prop; } public function unserialize($s) { $this->prop = $s; } } function var_export_normalized($value) { echo ltrim(var_export($value, true), "\\"); } $t = new Test(); $t->prop = str_repeat('0', 256); var_export_normalized(bin2hex($s = igbinary_serialize($t))); echo "\n"; var_export_normalized(igbinary_unserialize($s)); echo "\n"; $t->prop = str_repeat('0', 1 << 16); $t2 = igbinary_unserialize(igbinary_serialize($t)); var_dump($t2->prop === $t->prop); ?> --EXPECT-- '000000021704546573741e010030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030' Test::__set_state(array( 'prop' => '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', )) bool(true) igbinary-3.2.13/tests/igbinary_074.phpt0000664000175000017500000000206014366750307016766 0ustar tysontyson--TEST-- igbinary and not enough data for array --FILE-- --EXPECTF-- One byte Warning: igbinary_unserialize_array: end-of-data in %s on line 3 Two byte Warning: igbinary_unserialize_array: end-of-data in %s on line 5 Warning: igbinary_unserialize_array: end-of-data in %s on line 6 Warning: igbinary_unserialize_array: data size 0 smaller that requested array length 1. in %s on line 7 Four byte Warning: igbinary_unserialize_array: end-of-data in %s on line 9 Warning: igbinary_unserialize_array: end-of-data in %s on line 10 Warning: igbinary_unserialize_array: data size 0 smaller that requested array length 1. in %s on line 11 igbinary-3.2.13/tests/igbinary_075.phpt0000664000175000017500000000165714366750307017002 0ustar tysontyson--TEST-- igbinary and not enough data for array --FILE-- --EXPECTF-- string(18) "000000021701581400" One byte igbinary_unserialize_object_properties: end-of-data NULL Two byte igbinary_unserialize_object_properties: end-of-data NULL igbinary_unserialize_object_properties: end-of-data Four byte igbinary_unserialize_object_properties: end-of-data NULL igbinary_unserialize_object_properties: end-of-dataigbinary-3.2.13/tests/igbinary_076.phpt0000664000175000017500000000107214366750307016772 0ustar tysontyson--TEST-- igbinary and edge cases unserializing array keys --FILE-- true]))); // 3-byte string truncated in the middle of the array key var_dump(igbinary_unserialize("\x00\x00\x00\x02\x14\x01\x11\x03\x6b\x65")); // null instead of a string - skip over the entry var_dump(igbinary_unserialize("\x00\x00\x00\x02\x14\x01\x00")); ?> --EXPECTF-- string(24) "00000002140111036b657905" igbinary_unserialize_chararray: end-of-data NULL array(0) { } igbinary-3.2.13/tests/igbinary_077.phpt0000664000175000017500000000452514366750307017001 0ustar tysontyson--TEST-- igbinary and large arrays --FILE-- --EXPECTF-- string(2062) "0000000215010006000600060106010602060206030603060406040605060506060606060706070608060806090609060a060a060b060b060c060c060d060d060e060e060f060f06100610061106110612061206130613061406140615061506160616061706170618061806190619061a061a061b061b061c061c061d061d061e061e061f061f06200620062106210622062206230623062406240625062506260626062706270628062806290629062a062a062b062b062c062c062d062d062e062e062f062f06300630063106310632063206330633063406340635063506360636063706370638063806390639063a063a063b063b063c063c063d063d063e063e063f063f06400640064106410642064206430643064406440645064506460646064706470648064806490649064a064a064b064b064c064c064d064d064e064e064f064f06500650065106510652065206530653065406540655065506560656065706570658065806590659065a065a065b065b065c065c065d065d065e065e065f065f06600660066106610662066206630663066406640665066506660666066706670668066806690669066a066a066b066b066c066c066d066d066e066e066f066f06700670067106710672067206730673067406740675067506760676067706770678067806790679067a067a067b067b067c067c067d067d067e067e067f067f06800680068106810682068206830683068406840685068506860686068706870688068806890689068a068a068b068b068c068c068d068d068e068e068f068f06900690069106910692069206930693069406940695069506960696069706970698069806990699069a069a069b069b069c069c069d069d069e069e069f069f06a006a006a106a106a206a206a306a306a406a406a506a506a606a606a706a706a806a806a906a906aa06aa06ab06ab06ac06ac06ad06ad06ae06ae06af06af06b006b006b106b106b206b206b306b306b406b406b506b506b606b606b706b706b806b806b906b906ba06ba06bb06bb06bc06bc06bd06bd06be06be06bf06bf06c006c006c106c106c206c206c306c306c406c406c506c506c606c606c706c706c806c806c906c906ca06ca06cb06cb06cc06cc06cd06cd06ce06ce06cf06cf06d006d006d106d106d206d206d306d306d406d406d506d506d606d606d706d706d806d806d906d906da06da06db06db06dc06dc06dd06dd06de06de06df06df06e006e006e106e106e206e206e306e306e406e406e506e506e606e606e706e706e806e806e906e906ea06ea06eb06eb06ec06ec06ed06ed06ee06ee06ef06ef06f006f006f106f106f206f206f306f306f406f406f506f506f606f606f706f706f806f806f906f906fa06fa06fb06fb06fc06fc06fd06fd06fe06fe06ff06ff" bool(true) bool(true) igbinary-3.2.13/tests/igbinary_078.phpt0000664000175000017500000000111714366750307016774 0ustar tysontyson--TEST-- igbinary and large arrays --FILE-- prop = $value; } public function __sleep() { return null; } } var_dump(bin2hex($s = igbinary_serialize(new BadSleep('override')))); var_dump(igbinary_unserialize($s)); ?> --EXPECTF-- Notice: igbinary_serialize(): __sleep should return an array only containing the names of instance-variables to serialize in %s on line %d string(32) "000000021708426164536c6565701400" object(BadSleep)#1 (1) { ["prop"]=> string(1) "x" }igbinary-3.2.13/tests/igbinary_079.phpt0000664000175000017500000000201214366750307016770 0ustar tysontyson--TEST-- igbinary and many arrays --FILE-- --EXPECTF-- string(60) "000000021404060014010600060006011401060006010602010106030102" array(4) { [0]=> array(1) { [0]=> int(0) } [1]=> array(1) { [0]=> int(1) } [2]=> array(1) { [0]=> int(0) } [3]=> array(1) { [0]=> int(1) } } bool(true) bool(true) igbinary-3.2.13/tests/igbinary_080.phpt0000664000175000017500000000111314366750307016761 0ustar tysontyson--TEST-- igbinary with hash collision serializing strings --FILE-- "3010480803", 'user_id'=>12346]; $serialized = igbinary_serialize($var); $unserialized = igbinary_unserialize($serialized); var_dump($unserialized); $var=['id'=>"3010480803", 'user_id'=>"3010480804"]; $serialized = igbinary_serialize($var); $unserialized = igbinary_unserialize($serialized); var_dump($unserialized); ?> --EXPECT-- array(2) { ["id"]=> string(10) "3010480803" ["user_id"]=> int(12346) } array(2) { ["id"]=> string(10) "3010480803" ["user_id"]=> string(10) "3010480804" } igbinary-3.2.13/tests/igbinary_081.phpt0000664000175000017500000000207014366750307016765 0ustar tysontyson--TEST-- igbinary with reference group of size 1 created by array_walk_recursive --FILE-- env = &$e; } } $arrayObject = new ArrayObject(); $testClass = new TestClass(); $testClass->setEnv($arrayObject); var_dump(igbinary_unserialize(igbinary_serialize($testClass))); ?> --EXPECTF-- object(TestClass)#%d (1) { ["env":"TestClass":private]=> object(ArrayObject)#%d (1) { ["storage":"ArrayObject":private]=> array(0) { } } } igbinary-3.2.13/tests/igbinary_082_php74.phpt0000664000175000017500000000150314366750307020010 0ustar tysontyson--TEST-- igbinary object with typed properties with reference to ArrayObject --SKIPIF-- --INI-- ; Note that php 8.1 deprecates using Serializable without __serialize/__unserialize but we are testing Serialize for igbinary. Suppress deprecations. error_reporting=E_ALL & ~E_DEPRECATED --FILE-- env = &$e; } } $arrayObject = new ArrayObject(); $testClass = new TestClass(); $testClass->setEnv($arrayObject); var_dump(igbinary_unserialize(igbinary_serialize($testClass))); ?> --EXPECTF-- object(TestClass)#%d (1) { ["env":"TestClass":private]=> object(ArrayObject)#%d (1) { ["storage":"ArrayObject":private]=> array(0) { } } } igbinary-3.2.13/tests/igbinary_083.phpt0000664000175000017500000000165314366750307016775 0ustar tysontyson--TEST-- igbinary object with reference to ArrayObject --INI-- ; Note that php 8.1 deprecates using Serializable without __serialize/__unserialize but we are testing Serialize for igbinary. Suppress deprecations. error_reporting=E_ALL & ~E_DEPRECATED --FILE-- --EXPECT-- Called serialize %00%00%00%02%14%04%06%00%25%00%06%01%25%22%01%06%02%25%11%06endcap%06%03%25%01%02 array(4) { [0]=> &NULL [1]=> &NULL [2]=> &string(6) "endcap" [3]=> &string(6) "endcap" }igbinary-3.2.13/tests/igbinary_084.phpt0000664000175000017500000000112614366750307016771 0ustar tysontyson--TEST-- Properly free duplicate properties when unserializing invalid data --FILE-- pub = new Test(); $s = igbinary_serialize($t); echo urlencode($s), "\n"; $unser = igbinary_unserialize($s); var_dump($unser); ?> --EXPECT-- %00%00%00%02%17%04Test%14%02%11%03pub%1A%00%14%02%0E%01%00%0E%01%00%0E%01%22%01 object(Test)#3 (1) { ["pub"]=> object(Test)#4 (1) { ["pub"]=> NULL } } igbinary-3.2.13/tests/igbinary_084b.phpt0000664000175000017500000000164214366750307017136 0ustar tysontyson--TEST-- Properly free duplicate undeclared properties when unserializing invalid data --SKIPIF-- = 90000) { echo "skip requires php < 9.0 when testing that the deprecation has no impact on igbinary functionality\n"; } ?> --FILE-- = 80200) { require_once __DIR__ . '/php82_suppress_dynamic_properties_warning.inc'; } class Test { public function __construct( ) { $this->pub = null; } public function __sleep() { // TODO: Could start detecting duplicates and emitting a notice as well return ["pub", "pub"]; } } $t = new Test(); $t->pub = new Test(); $s = igbinary_serialize($t); echo urlencode($s), "\n"; $unser = igbinary_unserialize($s); var_dump($unser); ?> --EXPECT-- %00%00%00%02%17%04Test%14%02%11%03pub%1A%00%14%02%0E%01%00%0E%01%00%0E%01%22%01 object(Test)#3 (1) { ["pub"]=> object(Test)#4 (1) { ["pub"]=> NULL } } igbinary-3.2.13/tests/igbinary_085.phpt0000664000175000017500000000146214366750307016775 0ustar tysontyson--TEST-- Properly free unexpected duplicate fields when unserializing arrays --FILE-- $s, 'yy' => $s]); echo urlencode($ser), "\n"; $result = igbinary_unserialize(str_replace('xx', 'yy', $ser)); var_dump($result); $ser = igbinary_serialize([0x66 => $s, 0x77 => $s]); echo urlencode($ser), "\n"; $result2 = igbinary_unserialize(str_replace("\x66", "\x77", $ser)); var_dump($result2); ?> --EXPECT-- %00%00%00%02%14%02%11%02xx%17%08stdClass%14%00%11%02yy%22%01 array(1) { ["yy"]=> object(stdClass)#2 (0) { } } %00%00%00%02%14%02%06f%17%08stdClass%14%00%06w%22%01 array(1) { [119]=> object(stdClass)#3 (0) { } }igbinary-3.2.13/tests/igbinary_086.phpt0000664000175000017500000000137714366750307017003 0ustar tysontyson--TEST-- Test serializing many different classes --SKIPIF-- --FILE-- --EXPECT-- bool(true) object(C0)#1 (0) { } object(C67800)#679 (0) { } igbinary-3.2.13/tests/igbinary_087.phpt0000664000175000017500000001417114366750307017000 0ustar tysontyson--TEST-- Test serializing many values in __sleep --SKIPIF-- --FILE-- {"p$i"} = "$i"; } $ser = igbinary_serialize($x); $unser = igbinary_unserialize($ser); echo urlencode($ser), "\n"; var_dump($unser == $x); unset($unser); for ($i = 0; $i < 70000; $i++) { $x->{"p$i"} = "other$i"; } var_dump(igbinary_unserialize(igbinary_serialize($x)) == $x); ?> --EXPECT-- Called __sleep props=300 %00%00%00%02%17%01X%15%01%2C%11%02p0%11%010%11%02p1%11%011%11%02p2%11%012%11%02p3%11%013%11%02p4%11%014%11%02p5%11%015%11%02p6%11%016%11%02p7%11%017%11%02p8%11%018%11%02p9%11%019%11%03p10%11%0210%11%03p11%11%0211%11%03p12%11%0212%11%03p13%11%0213%11%03p14%11%0214%11%03p15%11%0215%11%03p16%11%0216%11%03p17%11%0217%11%03p18%11%0218%11%03p19%11%0219%11%03p20%11%0220%11%03p21%11%0221%11%03p22%11%0222%11%03p23%11%0223%11%03p24%11%0224%11%03p25%11%0225%11%03p26%11%0226%11%03p27%11%0227%11%03p28%11%0228%11%03p29%11%0229%11%03p30%11%0230%11%03p31%11%0231%11%03p32%11%0232%11%03p33%11%0233%11%03p34%11%0234%11%03p35%11%0235%11%03p36%11%0236%11%03p37%11%0237%11%03p38%11%0238%11%03p39%11%0239%11%03p40%11%0240%11%03p41%11%0241%11%03p42%11%0242%11%03p43%11%0243%11%03p44%11%0244%11%03p45%11%0245%11%03p46%11%0246%11%03p47%11%0247%11%03p48%11%0248%11%03p49%11%0249%11%03p50%11%0250%11%03p51%11%0251%11%03p52%11%0252%11%03p53%11%0253%11%03p54%11%0254%11%03p55%11%0255%11%03p56%11%0256%11%03p57%11%0257%11%03p58%11%0258%11%03p59%11%0259%11%03p60%11%0260%11%03p61%11%0261%11%03p62%11%0262%11%03p63%11%0263%11%03p64%11%0264%11%03p65%11%0265%11%03p66%11%0266%11%03p67%11%0267%11%03p68%11%0268%11%03p69%11%0269%11%03p70%11%0270%11%03p71%11%0271%11%03p72%11%0272%11%03p73%11%0273%11%03p74%11%0274%11%03p75%11%0275%11%03p76%11%0276%11%03p77%11%0277%11%03p78%11%0278%11%03p79%11%0279%11%03p80%11%0280%11%03p81%11%0281%11%03p82%11%0282%11%03p83%11%0283%11%03p84%11%0284%11%03p85%11%0285%11%03p86%11%0286%11%03p87%11%0287%11%03p88%11%0288%11%03p89%11%0289%11%03p90%11%0290%11%03p91%11%0291%11%03p92%11%0292%11%03p93%11%0293%11%03p94%11%0294%11%03p95%11%0295%11%03p96%11%0296%11%03p97%11%0297%11%03p98%11%0298%11%03p99%11%0299%11%04p100%11%03100%11%04p101%11%03101%11%04p102%11%03102%11%04p103%11%03103%11%04p104%11%03104%11%04p105%11%03105%11%04p106%11%03106%11%04p107%11%03107%11%04p108%11%03108%11%04p109%11%03109%11%04p110%11%03110%11%04p111%11%03111%11%04p112%11%03112%11%04p113%11%03113%11%04p114%11%03114%11%04p115%11%03115%11%04p116%11%03116%11%04p117%11%03117%11%04p118%11%03118%11%04p119%11%03119%11%04p120%11%03120%11%04p121%11%03121%11%04p122%11%03122%11%04p123%11%03123%11%04p124%11%03124%11%04p125%11%03125%11%04p126%11%03126%11%04p127%11%03127%11%04p128%11%03128%11%04p129%11%03129%11%04p130%11%03130%11%04p131%11%03131%11%04p132%11%03132%11%04p133%11%03133%11%04p134%11%03134%11%04p135%11%03135%11%04p136%11%03136%11%04p137%11%03137%11%04p138%11%03138%11%04p139%11%03139%11%04p140%11%03140%11%04p141%11%03141%11%04p142%11%03142%11%04p143%11%03143%11%04p144%11%03144%11%04p145%11%03145%11%04p146%11%03146%11%04p147%11%03147%11%04p148%11%03148%11%04p149%11%03149%11%04p150%11%03150%11%04p151%11%03151%11%04p152%11%03152%11%04p153%11%03153%11%04p154%11%03154%11%04p155%11%03155%11%04p156%11%03156%11%04p157%11%03157%11%04p158%11%03158%11%04p159%11%03159%11%04p160%11%03160%11%04p161%11%03161%11%04p162%11%03162%11%04p163%11%03163%11%04p164%11%03164%11%04p165%11%03165%11%04p166%11%03166%11%04p167%11%03167%11%04p168%11%03168%11%04p169%11%03169%11%04p170%11%03170%11%04p171%11%03171%11%04p172%11%03172%11%04p173%11%03173%11%04p174%11%03174%11%04p175%11%03175%11%04p176%11%03176%11%04p177%11%03177%11%04p178%11%03178%11%04p179%11%03179%11%04p180%11%03180%11%04p181%11%03181%11%04p182%11%03182%11%04p183%11%03183%11%04p184%11%03184%11%04p185%11%03185%11%04p186%11%03186%11%04p187%11%03187%11%04p188%11%03188%11%04p189%11%03189%11%04p190%11%03190%11%04p191%11%03191%11%04p192%11%03192%11%04p193%11%03193%11%04p194%11%03194%11%04p195%11%03195%11%04p196%11%03196%11%04p197%11%03197%11%04p198%11%03198%11%04p199%11%03199%11%04p200%11%03200%11%04p201%11%03201%11%04p202%11%03202%11%04p203%11%03203%11%04p204%11%03204%11%04p205%11%03205%11%04p206%11%03206%11%04p207%11%03207%11%04p208%11%03208%11%04p209%11%03209%11%04p210%11%03210%11%04p211%11%03211%11%04p212%11%03212%11%04p213%11%03213%11%04p214%11%03214%11%04p215%11%03215%11%04p216%11%03216%11%04p217%11%03217%11%04p218%11%03218%11%04p219%11%03219%11%04p220%11%03220%11%04p221%11%03221%11%04p222%11%03222%11%04p223%11%03223%11%04p224%11%03224%11%04p225%11%03225%11%04p226%11%03226%11%04p227%11%03227%11%04p228%11%03228%11%04p229%11%03229%11%04p230%11%03230%11%04p231%11%03231%11%04p232%11%03232%11%04p233%11%03233%11%04p234%11%03234%11%04p235%11%03235%11%04p236%11%03236%11%04p237%11%03237%11%04p238%11%03238%11%04p239%11%03239%11%04p240%11%03240%11%04p241%11%03241%11%04p242%11%03242%11%04p243%11%03243%11%04p244%11%03244%11%04p245%11%03245%11%04p246%11%03246%11%04p247%11%03247%11%04p248%11%03248%11%04p249%11%03249%11%04p250%11%03250%11%04p251%11%03251%11%04p252%11%03252%11%04p253%11%03253%11%04p254%11%03254%11%04p255%11%03255%11%04p256%11%03256%11%04p257%11%03257%11%04p258%11%03258%11%04p259%11%03259%11%04p260%11%03260%11%04p261%11%03261%11%04p262%11%03262%11%04p263%11%03263%11%04p264%11%03264%11%04p265%11%03265%11%04p266%11%03266%11%04p267%11%03267%11%04p268%11%03268%11%04p269%11%03269%11%04p270%11%03270%11%04p271%11%03271%11%04p272%11%03272%11%04p273%11%03273%11%04p274%11%03274%11%04p275%11%03275%11%04p276%11%03276%11%04p277%11%03277%11%04p278%11%03278%11%04p279%11%03279%11%04p280%11%03280%11%04p281%11%03281%11%04p282%11%03282%11%04p283%11%03283%11%04p284%11%03284%11%04p285%11%03285%11%04p286%11%03286%11%04p287%11%03287%11%04p288%11%03288%11%04p289%11%03289%11%04p290%11%03290%11%04p291%11%03291%11%04p292%11%03292%11%04p293%11%03293%11%04p294%11%03294%11%04p295%11%03295%11%04p296%11%03296%11%04p297%11%03297%11%04p298%11%03298%11%04p299%11%03299 bool(true) Called __sleep props=70000 bool(true)igbinary-3.2.13/tests/igbinary_088.phpt0000664000175000017500000000174514366750307017004 0ustar tysontyson--TEST-- Test serializing wrong values in __sleep --SKIPIF-- --FILE-- {"p$i"} = "name$i"; } $ser = igbinary_serialize($x); $unser = igbinary_unserialize($ser); echo str_replace(['\\', '%'], ['\\\\', '\x'], urlencode($ser)), "\n"; var_dump($unser); ?> --EXPECTF-- Notice: igbinary_serialize(): "name0" returned as member variable from __sleep() but does not exist in %s on line 12 Notice: igbinary_serialize(): "name1" returned as member variable from __sleep() but does not exist in %s on line 12 Notice: igbinary_serialize(): "name2" returned as member variable from __sleep() but does not exist in %s on line 12 \x00\x00\x00\x02\x17\x01X\x14\x03\x11\x05name0\x00\x11\x05name1\x00\x11\x05name2\x00 object(X)#2 (3) { ["name0"]=> NULL ["name1"]=> NULL ["name2"]=> NULL }igbinary-3.2.13/tests/igbinary_089.phpt0000664000175000017500000000161714366750307017003 0ustar tysontyson--TEST-- Test serializing string > 4G --INI-- memory_limit=15G display_errors=stderr error_reporting=E_ALL --CONFLICTS-- high_memory --SKIPIF-- --FILE-- --EXPECTF-- len=4200000009 0000000213fa56ea002a2a2a2a2a2a2a2a2a2a2a bool(true) Warning: igbinary_unserialize_chararray: end-of-data in %sigbinary_089.php on line 10 NULLigbinary-3.2.13/tests/igbinary_089_32bit.phpt0000664000175000017500000000075614366750307020011 0ustar tysontyson--TEST-- Test unserializing invalid 64-bit string header on 32-bit platform --INI-- display_errors=stderr error_reporting=E_ALL --CONFLICTS-- high_memory --SKIPIF-- 4) { print "skip requires 32-bit\n"; } ?> --FILE-- --EXPECTF-- Warning: igbinary_unserialize_chararray: %s in %sigbinary_089_32bit.php on line 3 NULLigbinary-3.2.13/tests/igbinary_090.phpt0000664000175000017500000000125414366750307016770 0ustar tysontyson--TEST-- Check for handling of IS_INDIRECT in arrays --FILE-- $value) { if (!in_array($key, ['globalVar', 'otherGlobalVar'])) { unset($x[$key]); } } var_dump($x); $ser = igbinary_serialize($x); echo urlencode($ser) . "\n"; var_dump(igbinary_unserialize($ser)); }); --EXPECT-- array(2) { ["globalVar"]=> &int(123) ["otherGlobalVar"]=> &int(123) } %00%00%00%02%14%02%11%09globalVar%25%06%7B%11%0EotherGlobalVar%25%01%01 array(2) { ["globalVar"]=> &int(123) ["otherGlobalVar"]=> &int(123) } igbinary-3.2.13/tests/igbinary_091.phpt0000664000175000017500000000151314366750307016767 0ustar tysontyson--TEST-- Check for handling of anonymous classes --INI-- error_reporting=E_ALL & ~E_DEPRECATED --FILE-- getMessage() . "\n"; } } check_serialize_throws(new class () {}); // TODO: Update behavior based on https://bugs.php.net/bug.php?id=81111 /** check_serialize_throws(new class () { public function __serialize() { return []; } public function __unserialize($value) { } }); */ check_serialize_throws(new class () implements Serializable { public function serialize() { return ''; } public function unserialize($ser) { return new self(); } }); ?> --EXPECTF-- Caught: Serialization of 'class@anonymous' is not allowed Caught: Serialization of '%s@anonymous' is not allowed igbinary-3.2.13/tests/igbinary_092.phpt0000664000175000017500000000177014366750307016775 0ustar tysontyson--TEST-- Object test, unserialize_callback_func --INI-- error_reporting=E_ALL --FILE-- b == 2 ? 'OK' : 'ERROR'; echo "\n"; } catch (Throwable $e) { printf("Caught %s: %s\n", get_class($e), $e->getMessage()); } } class MyUnserializer { public static function handleUnserialize(string $class) { throw new RuntimeException('handleUnserialize: Class not found: ' . $class); } } ini_set('unserialize_callback_func', strtoupper('MyUnserializer::handleUnserialize')); test('throwing_autoload', '0000000217034f626a140211016106011101620602', false); ?> --EXPECTF-- Caught RuntimeException: handleUnserialize: Class not found: Obj igbinary-3.2.13/tests/igbinary_093.phpt0000664000175000017500000000123014366750307016765 0ustar tysontyson--TEST-- Test refusing to serialize/unserialize unserializable anonymous classes --INI-- error_reporting=E_ALL --FILE-- getMessage() . "\n"; } } check_serialize_throws(new class () { public function __serialize() { return []; } public function __unserialize($value) { } }); check_serialize_throws(function () { }); ?> --EXPECTF-- Caught: Serialization of 'class@anonymous' is not allowed Caught: Serialization of 'Closure' is not allowed igbinary-3.2.13/tests/igbinary_094.phpt0000664000175000017500000000163414366750307016776 0ustar tysontyson--TEST-- Test refusing to serialize/unserialize unserializable internal classes --INI-- error_reporting=E_ALL --SKIPIF-- --FILE-- getMessage() . "\n"; } } class Something extends CURLFile { public function __serialize() { return []; } public function __unserialize($value) { return new self('file'); } } check_serialize_throws(new CURLFile('file')); check_serialize_throws(new Something('file')); ?> --EXPECTF-- Caught: Serialization of 'CURLFile' is not allowed Caught: Serialization of 'Something' is not allowed igbinary-3.2.13/tests/igbinary_095.phpt0000664000175000017500000000452614366750307017002 0ustar tysontyson--TEST-- Test handling php 8.1 readonly properties --SKIPIF-- --FILE-- var = $intersection; } } class Y { public readonly mixed $var; public function __construct( public readonly int $a, private readonly ArrayAccess&Countable $intersection, protected readonly ?string $default = null, ) { $this->var = $intersection; } public function __serialize(): array { return [ 'a' => $this->a, 'intersection' => $this->intersection, 'default' => $this->default, 'var' => $this->var, ]; } public function __unserialize(array $data) { [ 'a' => $this->a, 'intersection' => $this->intersection, 'default' => $this->default, 'var' => $this->var, ] = $data; } } $ser = igbinary_serialize(new X(1, new ArrayObject())); echo urlencode($ser), "\n"; var_dump(igbinary_unserialize($ser)); $ser = igbinary_serialize(new Y(1, new ArrayObject())); echo urlencode($ser), "\n"; var_dump(igbinary_unserialize($ser)); ?> --EXPECT-- %00%00%00%02%17%01X%14%04%11%03var%17%0BArrayObject%14%04%06%00%06%00%06%01%14%00%06%02%14%00%06%03%00%11%01a%06%01%11%0F%00X%00intersection%22%01%11%0A%00%2A%00default%00 object(X)#1 (4) { ["var"]=> object(ArrayObject)#2 (1) { ["storage":"ArrayObject":private]=> array(0) { } } ["a"]=> int(1) ["intersection":"X":private]=> object(ArrayObject)#2 (1) { ["storage":"ArrayObject":private]=> array(0) { } } ["default":protected]=> NULL } %00%00%00%02%17%01Y%14%04%11%01a%06%01%11%0Cintersection%17%0BArrayObject%14%04%06%00%06%00%06%01%14%00%06%02%14%00%06%03%00%11%07default%00%11%03var%22%01 object(Y)#1 (4) { ["var"]=> object(ArrayObject)#2 (1) { ["storage":"ArrayObject":private]=> array(0) { } } ["a"]=> int(1) ["intersection":"Y":private]=> object(ArrayObject)#2 (1) { ["storage":"ArrayObject":private]=> array(0) { } } ["default":protected]=> NULL } igbinary-3.2.13/tests/igbinary_096.phpt0000664000175000017500000000100114366750307016764 0ustar tysontyson--TEST-- Test unserialization creates valid current() --INI-- display_errors=stderr error_reporting=E_ALL --FILE-- [ 'two' => [ 'three' => [ 'four' ], ], ], ]; $test = current(current(current(current($arr)))); var_dump($test); // Note: after unserialization the current is the last array element. $arr2 = igbinary_unserialize(igbinary_serialize($arr)); $test2 = current(current(current(current($arr2)))); var_dump($test2); ?> --EXPECT-- string(4) "four" string(4) "four"igbinary-3.2.13/tests/igbinary_097.phpt0000664000175000017500000000207114366750307016775 0ustar tysontyson--TEST-- Test serialize globals --SKIPIF-- = 80100) print "skip php >= 8.1\n"; // https://wiki.php.net/rfc/restrict_globals_usage ?> --FILE-- $_) { if ($key !== 'GLOBALS') { unset($GLOBALS[$key]); } } $ser = igbinary_serialize($GLOBALS); echo urlencode($ser) . "\n"; var_dump(igbinary_unserialize($ser)); $GLOBALS['globalVar'] = new stdClass(); $ser = igbinary_serialize($GLOBALS); echo urlencode($ser) . "\n"; var_dump(igbinary_unserialize($ser)); }); --EXPECTF-- %00%00%00%02%14%01%11%07GLOBALS%14%01%0E%00%01%01 array(1) { ["GLOBALS"]=> array(1) { ["GLOBALS"]=> *RECURSION* } } %00%00%00%02%14%02%11%07GLOBALS%14%02%0E%00%01%01%11%09globalVar%17%08stdClass%14%00%0E%01%22%02 array(2) { ["GLOBALS"]=> array(2) { ["GLOBALS"]=> *RECURSION* ["globalVar"]=> object(stdClass)#3 (0) { } } ["globalVar"]=> object(stdClass)#3 (0) { } } igbinary-3.2.13/tests/igbinary_098.phpt0000664000175000017500000000167414366750307017006 0ustar tysontyson--TEST-- Test PHP 8.2 readonly classes --SKIPIF-- --FILE-- a = 8; return $c; } } $c = new C(); $ser = igbinary_serialize($c); echo urlencode($ser), "\n"; var_dump(igbinary_unserialize($ser)); $c = C::create(); $ser = igbinary_serialize($c); echo urlencode($ser), "\n"; var_dump(igbinary_unserialize($ser)); $invalid1 = str_replace('a', 'b', $ser); try { var_dump(igbinary_unserialize($invalid1)); } catch (Error $e) { printf("%s: %s\n", $e::class, $e->getMessage()); } --EXPECT-- %00%00%00%02%17%01C%14%01%00 object(C)#2 (0) { ["a"]=> uninitialized(int) } %00%00%00%02%17%01C%14%01%11%01a%06%08 object(C)#1 (1) { ["a"]=> int(8) } Error: Cannot create dynamic property C::$b in igbinary_unserialize igbinary-3.2.13/tests/igbinary_099.phpt0000664000175000017500000000161414366750307017001 0ustar tysontyson--TEST-- Test PHP 8.2 deprecation of creation of dynamic properties --SKIPIF-- --FILE-- getMessage()); } } --EXPECTF-- Deprecated: igbinary_unserialize(): Creation of dynamic property C::$a is deprecated in %sigbinary_099.php on line 12 object(C)#2 (1) { ["a"]=> int(8) } Deprecated: igbinary_unserialize(): Creation of dynamic property C::$a is deprecated in %sigbinary_099.php on line 12 object(C)#2 (1) { ["a":protected]=> int(8) } igbinary-3.2.13/tests/igbinary_bug54662.phpt0000664000175000017500000000104514366750307017642 0ustar tysontyson--TEST-- Nested objects cause segfault, php bug #54662 --SKIPIF-- --FILE-- append(new Storage); $ser = igbinary_serialize($collection); $new_collection = igbinary_unserialize($ser); var_dump($new_collection[0]->storage); --EXPECT-- string(8) "a string" igbinary-3.2.13/tests/igbinary_bug72134.phpt0000664000175000017500000000104514366750307017634 0ustar tysontyson--TEST-- igbinary_unserialize regression test for segfault on 3rd call for objects with dynamic property --FILE-- i = 1; $igb = igbinary_serialize($value); for ($i=0; $i < 30; $i++) { // This used to segfault at the third attempt echo igbinary_unserialize($igb)->bar . PHP_EOL; } --EXPECT-- test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test igbinary-3.2.13/tests/igbinary_enums_1.phpt0000664000175000017500000000177714366750307020041 0ustar tysontyson--TEST-- Test unserializing valid enums --SKIPIF-- --FILE-- 'Diamonds']); echo urlencode($serArray), "\n"; var_dump(igbinary_unserialize($serArray)); ?> --EXPECT-- %00%00%00%02%17%04Suit%27%11%06Hearts enum(Suit::Hearts) enum(Suit::Hearts) %00%00%00%02%14%06%06%00%17%04Suit%27%11%06Hearts%06%01%1A%00%27%11%08Diamonds%06%02%1A%00%27%11%06Spades%06%03%1A%00%27%11%05Clubs%06%04%22%04%0E%02%0E%02 array(6) { [0]=> enum(Suit::Hearts) [1]=> enum(Suit::Diamonds) [2]=> enum(Suit::Spades) [3]=> enum(Suit::Clubs) [4]=> enum(Suit::Clubs) ["Diamonds"]=> string(8) "Diamonds" } igbinary-3.2.13/tests/igbinary_enums_2.phpt0000664000175000017500000000346714366750307020040 0ustar tysontyson--TEST-- Test unserializing valid and invalid enums --SKIPIF-- --FILE-- Suit::Hearts]; $arr[1] = &$arr['Hearts']; $serArray = igbinary_serialize($arr); // PHP 8.1 added support for %0 as a null byte in EXPECTF in https://github.com/php/php-src/pull/7069 // Igbinary's use case of urlencode on binary data is rare. // So replace % with \x echo str_replace(['\\', '%'], ['\\\\', '\x'], urlencode($serArray)), "\n"; $result = igbinary_unserialize($serArray); var_dump($result); $result[1] = 'new'; var_dump($result); $serInvalid = str_replace('Hearts', 'HEARTS', $serArray); var_dump(igbinary_unserialize($serInvalid)); $serInvalidConst = str_replace('Hearts', 'vvvvvv', $serArray); var_dump(igbinary_unserialize($serInvalidConst)); $serMissingClass = str_replace('Suit', 'Club', $serArray); var_dump(igbinary_unserialize($serMissingClass)); $serInvalidClass = str_replace('Suit', 'ABCD', $serArray); var_dump(igbinary_unserialize($serInvalidClass)); ?> --EXPECTF-- \x00\x00\x00\x02\x14\x02\x11\x06Hearts\x25\x17\x04Suit\x27\x0E\x00\x06\x01\x25\x22\x01 array(2) { ["Hearts"]=> &enum(Suit::Hearts) [1]=> &enum(Suit::Hearts) } array(2) { ["Hearts"]=> &string(3) "new" [1]=> &string(3) "new" } Warning: igbinary_unserialize_object_enum_case: Suit::HEARTS is not an enum case in %s on line 25 NULL Warning: igbinary_unserialize_object_enum_case: Undefined constant Suit::vvvvvv in %s on line 28 NULL Warning: igbinary_unserialize_object_enum_case: Class 'Club' does not exist in %s on line 31 NULL Warning: igbinary_unserialize_object_enum_case: Class 'ABCD' is not an enum in %s on line 34 NULLigbinary-3.2.13/tests/igbinary_unserialize_v1_compatible.phpt0000664000175000017500000000527614366750307023627 0ustar tysontyson--TEST-- Unserialize backwards compatible with v1. --INI-- pcre.jit=0 --FILE-- 'b:1;', 'type' => 'boolean', 'description' => 'bool true', 'data' => 'AAAAAQU=', 'version' => 1, ), array( 'var' => 'b:0;', 'type' => 'boolean', 'description' => 'bool false', 'data' => 'AAAAAQQ=', 'version' => 1, ), array( 'var' => 'd:1.2881887378882661554513333612703718245029449462890625;', 'type' => 'double', 'description' => 'double', 'data' => 'AAAAAQw/9Jxry0Tj0Q==', 'version' => 1, ), array( 'var' => 'i:29913;', 'type' => 'integer', 'description' => 'int', 'data' => 'AAAAAQh02Q==', 'version' => 1, ), array( 'var' => 'N;', 'type' => 'NULL', 'description' => 'null', 'data' => 'AAAAAQA=', 'version' => 1, ), array( 'var' => 's:0:"";', 'type' => 'string', 'description' => 'string', 'data' => 'AAAAAQ0=', 'version' => 1, ), array( 'var' => 's:13:"asdf' . "\0" . 'asdfasdf";', 'type' => 'string', 'description' => 'string', 'data' => 'AAAAARENYXNkZgBhc2RmYXNkZg==', 'version' => 1, ), array( 'var' => 'a:4:{i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;}', 'type' => 'array', 'description' => 'array', 'data' => 'AAAAARQEBgAGAQYBBgIGAgYDBgMGBA==', 'version' => 1, ), array( 'var' => 'O:8:"stdClass":4:{s:1:"a";i:1;s:1:"b";i:2;s:1:"c";i:3;s:1:"d";i:4;}', 'var_e' => (object)array("a"=>1, "b"=>2, "c"=>3, "d"=>4), 'type' => 'object', 'description' => 'object', 'data' => 'AAAAAhcIc3RkQ2xhc3MUBBEBYQYBEQFiBgIRAWMGAxEBZAYE', 'version' => 1, ), array( 'var' => 'a:2:{i:0;a:3:{i:0;s:1:"a";i:1;s:1:"b";i:2;s:1:"c";}i:1;R:2;}', 'type' => 'array', 'description' => 'reference', 'data' => 'AAAAARQCBgAUAwYAEQFhBgERAWIGAhEBYwYBAQE=', 'version' => 1, ), ); $all_passed = true; foreach ($data as $item) { if (isset($item['var_e'])) { $var = $item['var_e']; } else { $var = unserialize($item['var']); } $unserialized = igbinary_unserialize(base64_decode($item['data'])); ob_start(); var_dump($var); $dump_expected = ob_get_clean(); ob_start(); var_dump($unserialized); $dump_actual = ob_get_clean(); // replace all object ids to 0 $dump_expected = preg_replace('/#\d+/', '#0', $dump_expected); $dump_actual = preg_replace('/#\d+/', '#0', $dump_actual); if ($dump_expected !== $dump_actual) { if ($item['description'] == 'reference') { echo "reference deserialization works, but the result is not a reference.\n"; continue; } echo "Differing unserialized: {$item['description']}\n"; echo "Expected:\n", $dump_expected, "\n"; echo "Actual:\n", $dump_actual, "\n"; } } echo $all_passed ? 'OK' : 'ERROR', "\n"; --EXPECT-- reference deserialization works, but the result is not a reference. OK igbinary-3.2.13/tests/php82_suppress_dynamic_properties_warning.inc0000664000175000017500000000124514366750307025004 0ustar tysontyson --EXPECT-- %00%00%00%02%17%04Test%14%02%11%0A%00Test%00priv%00%0E%01%00 object(Test)#1 (1) { ["priv":"Test":private]=> NULL } igbinary-3.2.13/tests/typed_property_ref_assignment_failure.phpt0000664000175000017500000000133514366750307024460 0ustar tysontyson--TEST-- Failure to assign ref to typed property --SKIPIF-- --FILE-- prop = new stdClass(); $b->prop->y = &$b->prop; $ser = igbinary_serialize($b); echo str_replace('%', '\\x', urlencode($ser)), "\n"; // Should reject stdClass reference $ser2 = str_replace('Best', 'Test', $ser); try { var_dump(igbinary_unserialize($ser2)); } catch (Error $e) { printf("Caught %s: %s\n", get_class($e), $e->getMessage()); } ?> --EXPECTF-- \x00\x00\x00\x02\x17\x04Best\x14\x01\x11\x04prop\x25\x17\x08stdClass\x14\x01\x11\x01y\x25\x22\x01 Caught TypeError: %s property Test::$prop %s igbinary-3.2.13/tests/typed_property_ref_overwrite.phpt0000664000175000017500000000141014366750307022621 0ustar tysontyson--TEST-- Overwriting a typed property reference --SKIPIF-- --FILE-- prop = $orig; $orig->drop = null; $ser = igbinary_serialize($orig); unset($orig); gc_collect_cycles(); // also tests that igbinary properly marks unserialized data as a possible gc cycle root. var_dump(igbinary_unserialize($ser)); gc_collect_cycles(); echo "Test overwrite reference group\n"; $ser2 = str_replace('drop', 'prop', $ser); var_dump(igbinary_unserialize($ser2)); ?> --EXPECT-- object(Test)#1 (2) { ["prop"]=> *RECURSION* ["drop"]=> NULL } Test overwrite reference group object(Test)#1 (2) { ["prop"]=> NULL ["drop"]=> NULL } igbinary-3.2.13/tests/typed_property_ref_overwrite2.phpt0000664000175000017500000000150114366750307022704 0ustar tysontyson--TEST-- Overwriting a typed property that is not yet a reference --SKIPIF-- --FILE-- drop = null; $orig->prop = new Test(); $orig->prop->prop = &$orig->prop; $ser = igbinary_serialize($orig); echo urlencode($ser), "\n"; $ser2 = str_replace('drop', 'prop', $ser); $result = igbinary_unserialize($ser2); var_dump($result); try { $result->prop = 1; } catch (TypeError $e) { printf("Caught %s\n", get_class($e)); } ?> --EXPECT-- %00%00%00%02%17%04Test%14%02%11%04drop%00%11%04prop%25%1A%00%14%02%0E%01%00%0E%02%25%22%01 object(Test)#3 (2) { ["drop"]=> NULL ["prop"]=> &object(Test)#4 (2) { ["drop"]=> NULL ["prop"]=> *RECURSION* } } Caught TypeError igbinary-3.2.13/tests/typed_property_refs.phpt0000664000175000017500000000446414366750307020712 0ustar tysontyson--TEST-- unserialize with references to typed properties shall skip the references or fail --SKIPIF-- --FILE-- a = $value; $v->b = &$v->a; $ser = igbinary_serialize($v); printf("for %s: %s\n", var_export($value, true), urlencode($ser)); return $ser; } $serInt = create_ser(1); $serNull = create_ser(null); $serStr = create_ser('x'); var_dump(igbinary_unserialize(str_replace('X', 'A', $serInt))); var_dump(igbinary_unserialize(str_replace('X', 'B', $serInt))); var_dump(igbinary_unserialize(str_replace('X', 'E', $serInt))); try { var_dump(igbinary_unserialize(str_replace('X', 'A', $serNull))); } catch (TypeError $e) { echo $e->getMessage(), "\n"; } try { var_dump(igbinary_unserialize(str_replace('X', 'B', $serNull))); } catch (TypeError $e) { echo $e->getMessage(), "\n"; } try { var_dump(igbinary_unserialize(str_replace('X', 'C', $serInt))); } catch (TypeError $e) { echo $e->getMessage(), "\n"; } try { var_dump(igbinary_unserialize(str_replace('X', 'C', $serStr))); } catch (TypeError $e) { echo $e->getMessage(), "\n"; } try { var_dump(igbinary_unserialize(str_replace('X', 'D', $serInt))); } catch (TypeError $e) { echo $e->getMessage(), "\n"; } ?> --EXPECT-- for 1: %00%00%00%02%17%01X%14%02%11%01a%25%06%01%11%01b%25%01%01 for NULL: %00%00%00%02%17%01X%14%02%11%01a%25%00%11%01b%25%01%01 for 'x': %00%00%00%02%17%01X%14%02%11%01a%25%11%01x%11%01b%25%01%01 object(A)#1 (2) { ["a"]=> &int(1) ["b"]=> &int(1) } object(B)#1 (2) { ["a"]=> &int(1) ["b"]=> &int(1) } object(E)#1 (2) { ["a"]=> &int(1) ["b"]=> &int(1) } Cannot assign null to property A::$a of type int Cannot assign null to property B::$b of type int Cannot assign int to property C::$b of type string Cannot assign string to property C::$a of type int Reference with value of type int held by property D::$a of type int is not compatible with property D::$b of type float igbinary-3.2.13/tests/typed_property_refs_php74.phpt0000664000175000017500000000451014366750307021724 0ustar tysontyson--TEST-- unserialize with references to typed properties shall skip the references or fail --SKIPIF-- = 80000) die("skip requires php 7.4 error message"); ?> --FILE-- a = $value; $v->b = &$v->a; $ser = igbinary_serialize($v); printf("for %s: %s\n", var_export($value, true), urlencode($ser)); return $ser; } $serInt = create_ser(1); $serNull = create_ser(null); $serStr = create_ser('x'); var_dump(igbinary_unserialize(str_replace('X', 'A', $serInt))); var_dump(igbinary_unserialize(str_replace('X', 'B', $serInt))); var_dump(igbinary_unserialize(str_replace('X', 'E', $serInt))); try { var_dump(igbinary_unserialize(str_replace('X', 'A', $serNull))); } catch (TypeError $e) { echo $e->getMessage(), "\n"; } try { var_dump(igbinary_unserialize(str_replace('X', 'B', $serNull))); } catch (TypeError $e) { echo $e->getMessage(), "\n"; } try { var_dump(igbinary_unserialize(str_replace('X', 'C', $serInt))); } catch (TypeError $e) { echo $e->getMessage(), "\n"; } try { var_dump(igbinary_unserialize(str_replace('X', 'C', $serStr))); } catch (TypeError $e) { echo $e->getMessage(), "\n"; } try { var_dump(igbinary_unserialize(str_replace('X', 'D', $serInt))); } catch (TypeError $e) { echo $e->getMessage(), "\n"; } ?> --EXPECT-- for 1: %00%00%00%02%17%01X%14%02%11%01a%25%06%01%11%01b%25%01%01 for NULL: %00%00%00%02%17%01X%14%02%11%01a%25%00%11%01b%25%01%01 for 'x': %00%00%00%02%17%01X%14%02%11%01a%25%11%01x%11%01b%25%01%01 object(A)#1 (2) { ["a"]=> &int(1) ["b"]=> &int(1) } object(B)#1 (2) { ["a"]=> &int(1) ["b"]=> &int(1) } object(E)#1 (2) { ["a"]=> &int(1) ["b"]=> &int(1) } Typed property A::$a must be int, null used Typed property B::$b must be int, null used Typed property C::$b must be string, int used Typed property C::$a must be int, string used Reference with value of type int held by property D::$a of type int is not compatible with property D::$b of type float igbinary-3.2.13/config.m40000664000175000017500000000535314366750307014250 0ustar tysontysondnl config.m4 for extension igbinary dnl Comments in this file start with the string 'dnl'. dnl Remove where necessary. This file will not work dnl without editing. dnl If your extension references something external, use with: dnl PHP_ARG_WITH(igbinary, for igbinary support, dnl Make sure that the comment is aligned: dnl [ --with-igbinary Include igbinary support]) dnl Otherwise use enable: PHP_ARG_ENABLE(igbinary, whether to enable igbinary support, [ --enable-igbinary Enable igbinary support]) if test "$PHP_IGBINARY" != "no"; then AC_CHECK_HEADERS([stdbool.h],, AC_MSG_ERROR([stdbool.h not exists])) AC_CHECK_HEADERS([stddef.h],, AC_MSG_ERROR([stddef.h not exists])) AC_CHECK_HEADERS([stdint.h],, AC_MSG_ERROR([stdint.h not exists])) AC_MSG_CHECKING(PHP version) PHP_IGBINARY_SRC_FILES="src/php7/igbinary.c src/php7/hash_si.c src/php7/hash_si_ptr.c" if test -n "$phpincludedir" -a -d "$phpincludedir"; then IGBINARY_PHPINCLUDEDIR=$phpincludedir else IGBINARY_PHPINCLUDEDIR=$abs_srcdir fi AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <$IGBINARY_PHPINCLUDEDIR/main/php_version.h> ]], [[ #if PHP_MAJOR_VERSION < 7 #error PHP < 7 #endif ]])],[ AC_MSG_RESULT([PHP 7 or newer]) ],[ AC_MSG_ERROR([PHP 5 is not supported by igbinary 3. Use igbinary 2 instead for PHP5 support.]) ]) AC_MSG_CHECKING([for APCu includes]) if test -f "$IGBINARY_PHPINCLUDEDIR/ext/apcu/apc_serializer.h"; then apc_inc_path="$IGBINARY_PHPINCLUDEDIR" AC_MSG_RESULT([APCu in $apc_inc_path]) AC_DEFINE(HAVE_APCU_SUPPORT,1,[Whether to enable APCu support]) else AC_MSG_RESULT([not found]) fi AC_CHECK_SIZEOF([long]) dnl GCC AC_MSG_CHECKING(compiler type) if test ! -z "`$CC --version | grep -i CLANG`"; then AC_MSG_RESULT(clang) if test -z "`echo $CFLAGS | grep -- -O0`"; then PHP_IGBINARY_CFLAGS="$CFLAGS -Wall -O2" fi elif test "$GCC" = yes; then AC_MSG_RESULT(gcc) if test -z "`echo $CFLAGS | grep -- '-O[0123]'`"; then PHP_IGBINARY_CFLAGS="$CFLAGS -O2 -Wall -Wpointer-arith -Wcast-align -Wwrite-strings -Wswitch" fi elif test "$ICC" = yes; then AC_MSG_RESULT(icc) if test -z "`echo $CFLAGS | grep -- -O0`"; then PHP_IGBINARY_CFLAGS="$CFLAGS -no-prec-div -O3 -x0 -unroll2" fi else AC_MSG_RESULT(other) fi PHP_ADD_MAKEFILE_FRAGMENT(Makefile.bench) PHP_INSTALL_HEADERS([ext/igbinary], [igbinary.h src/php7/igbinary.h php_igbinary.h src/php7/php_igbinary.h]) PHP_NEW_EXTENSION(igbinary, $PHP_IGBINARY_SRC_FILES, $ext_shared,, $PHP_IGBINARY_CFLAGS) PHP_ADD_EXTENSION_DEP(igbinary, session, true) AC_DEFINE(HAVE_IGBINARY, 1, [Have igbinary support]) PHP_ADD_BUILD_DIR($abs_builddir/src/php7, 1) PHP_SUBST(IGBINARY_SHARED_LIBADD) fi igbinary-3.2.13/config.w320000664000175000017500000000343514366750307014342 0ustar tysontyson// vim:ft=javascript ARG_ENABLE("igbinary", "whether to enable igbinary support", "no"); if (PHP_IGBINARY == "yes") { var dll = get_define('PHPDLL'); var is_php5 = null != dll.match(/^php5/); // php 8.x also uses src/php7 var is_php7_or_newer = !is_php5 && null != dll.match(/^php[78]/) if (CHECK_HEADER_ADD_INCLUDE("apc_serializer.h", "CFLAGS_IGBINARY", "..\\pecl\\apcu;ext\\apcu")) { AC_DEFINE('HAVE_APCU_SUPPORT', 1, "Whether to enable apcu support"); if (!CHECK_HEADER_ADD_INCLUDE("apc_serializer.h", "CFLAGS_IGBINARY", "ext\\apcu")) { // Workaround to allow configuring and making apcu and igbinary at the same time. // If they aren't available in ext/apcu, expect them in ../pecl/apcu AC_DEFINE('HAVE_APCU_HEADERS_IN_PECL', 1, "Whether or not apcu headers exist only in pecl folder") } } var old_conf_dir = configure_module_dirname; var php_igbinary_src_files; var subdir; /* Copied from solr config.w32 */ /* XXX tricky job here, override configure_module_dirname, define the basic extension, then set it back*/ if (is_php5) { ERROR("PHP 5 is not supported by igbinary 3 - Use igbinary 2.x"); } else if (is_php7_or_newer) { subdir = "src\\php7"; php_igbinary_src_files = "igbinary.c hash_si.c hash_si_ptr.c" } else { ERROR("Cannot match any known PHP version with '" + dll + "'"); } configure_module_dirname = configure_module_dirname + "\\" + subdir; EXTENSION("igbinary", php_igbinary_src_files); configure_module_dirname = old_conf_dir; AC_DEFINE('HAVE_IGBINARY', 1, 'Have igbinary support', false); ADD_EXTENSION_DEP('igbinary', 'session', true); PHP_INSTALL_HEADERS('ext/igbinary', 'igbinary.h php_igbinary.h ' + subdir + '\\igbinary.h ' + subdir + '\\php_igbinary.h'); } igbinary-3.2.13/igbinary.h0000664000175000017500000000040514366750307014507 0ustar tysontyson#ifndef PHPEXT_IGBINARY_BASE_IGBINARY_H #define PHPEXT_IGBINARY_BASE_IGBINARY_H #include "php_version.h" #if PHP_MAJOR_VERSION == 7 || PHP_MAJOR_VERSION == 8 #include "src/php7/igbinary.h" #else #error "Unsupported php version for igbinary build" #endif #endif igbinary-3.2.13/php_igbinary.h0000664000175000017500000000142414366750307015360 0ustar tysontyson#ifndef PHPEXT_IGBINARY_BASE_PHP_IGBINARY_H #define PHPEXT_IGBINARY_BASE_PHP_IGBINARY_H #include "php_version.h" #if PHP_MAJOR_VERSION == 7 || PHP_MAJOR_VERSION == 8 #include "ext/igbinary/src/php7/php_igbinary.h" #else #error "Unsupported php version for igbinary build" #endif /** * The below line is redundant * (and just mentioning phpext_ in a comment is sufficient). * This block is only here to make php-src/build/print_include.awk include this file, * when igbinary is placed in php-src/ext/igbinary/ (instead of compiled separately with `phpize; ...`) */ #ifndef phpext_igbinary_ptr extern zend_module_entry igbinary_module_entry; #define phpext_igbinary_ptr &igbinary_module_entry #endif /** End line needed for putting igbinary in php-src/ext/igbinary to work */ #endif igbinary-3.2.13/src/php7/hash.h0000664000175000017500000000764414366750307015307 0ustar tysontyson/* +----------------------------------------------------------------------+ | See COPYING file for further copyright information | +----------------------------------------------------------------------+ | Author: Oleg Grenrus | | See CREDITS for contributors | +----------------------------------------------------------------------+ */ #ifndef HASH_H #define HASH_H #include #ifdef PHP_WIN32 # include "ig_win32.h" #else # include /* defines uint32_t etc */ #endif #include #include "zend_types.h" /** Key/value pair of hash_si. * @author Oleg Grenrus * @see hash_si */ struct hash_si_pair { zend_string *key_zstr; /* Contains key, key length, and key hash */ uint32_t key_hash; /**< Copy of ZSTR_H(key_zstr) (or 1 if hash is truncated to 0). Avoid dereferencing key_zstr if hashes are different. */ uint32_t value; /**< Value. */ }; enum hash_si_code { hash_si_code_inserted, hash_si_code_exists, hash_si_code_exception }; struct hash_si_result { enum hash_si_code code; uint32_t value; }; /** Hash-array. * Like c++ unordered_map. * Current implementation uses linear probing (with interval 1, 3, 5, or 7). * @author Oleg Grenrus */ struct hash_si { size_t mask; /**< Bitmask for the array. size == mask+1 */ size_t used; /**< Used size of array. */ struct hash_si_pair *data; /**< Pointer to array or pairs of data. */ }; /** Inits hash_si structure. * @param h pointer to hash_si struct. * @param size initial size of the hash array. * @return 0 on success, 1 else. */ int hash_si_init(struct hash_si *h, uint32_t size); /** Frees hash_si structure. * Doesn't call free(h). * @param h pointer to hash_si struct. */ void hash_si_deinit(struct hash_si *h); /** Inserts value into hash_si. * @param h Pointer to hash_si struct. * @param key Pointer to key. * @param key_len Key length. * @param value Value. * @return 0 on success, 1 or 2 else. */ /* int hash_si_insert (struct hash_si *h, const char *key, size_t key_len, uint32_t value); */ /** Finds value from hash_si. * Value returned through value param. * @param h Pointer to hash_si struct. * @param key Pointer to key. * @param key_len Key length. * @param[out] value Found value. * @return 0 if found, 1 if not. */ /* int hash_si_find (struct hash_si *h, const char *key, size_t key_len, uint32_t * value); */ /** Finds value from hash_si. * Value returned through value param. * @param h Pointer to hash_si struct. * @param key zend_string with key * @param[out] value Found value. * @return 0 if found, 1 if not. */ struct hash_si_result hash_si_find_or_insert(struct hash_si *h, zend_string *key, uint32_t value); /** Remove value from hash_si. * Removed value is available through value param. * @param h Pointer to hash_si struct. * @param key Pointer to key. * @param key_len Key length. * @param[out] value Removed value. * @return 0 ivalue removed, 1 if not existed. */ /* int hash_si_remove (struct hash_si *h, const char *key, size_t key_len, uint32_t * value); */ /** Travarses hash_si. * Calls traverse_function on every item. Traverse function should not modify hash * @param h Pointer to hash_si struct. * @param traverse_function Function to call on every item of hash_si. */ /* void hash_si_traverse (struct hash_si *h, int (*traverse_function) (const char *key, size_t key_len, uint32_t value)); */ /** Returns size of hash_si. * @param h Pointer to hash_si struct. * @return Size of hash_si. */ static zend_always_inline size_t hash_si_size(struct hash_si *h) { return h->used; } /** Returns capacity of hash_si. * @param h Pointer to hash_si struct. * @return Capacity of hash_si. */ static zend_always_inline size_t hash_si_capacity(struct hash_si *h) { return h->mask + 1; } #endif /* HASH_H */ igbinary-3.2.13/src/php7/hash_ptr.h0000664000175000017500000000666514366750307016176 0ustar tysontyson/* +----------------------------------------------------------------------+ | See COPYING file for further copyright information | +----------------------------------------------------------------------+ | Author: Oleg Grenrus | | See CREDITS for contributors | | This defines hash_si_ptr. | | It is like hash_si, but the key is always a non-zero zend_uintptr_t | +----------------------------------------------------------------------+ */ #ifndef HASH_PTR_H #define HASH_PTR_H #include #ifdef PHP_WIN32 # include "ig_win32.h" #else # include /* defines uint32_t etc */ #endif #include #include "zend_types.h" // NULL converted to an integer, on sane platforms. #define HASH_PTR_KEY_INVALID 0 /** Key/value pair of hash_si_ptr. * @author Oleg Grenrus * @see hash_si_ptr */ struct hash_si_ptr_pair { zend_uintptr_t key; /**< The key: The address of a pointer, casted to an int (won't be dereferenced). */ uint32_t value; /**< Value. */ }; /** Hash-array. * Like c++ std::unordered_map, but does not allow HASH_PTR_KEY_INVALID as a key. * Current implementation uses linear probing. * @author Oleg Grenrus */ struct hash_si_ptr { size_t size; /**< Allocated size of array. */ size_t used; /**< Used size of array. */ struct hash_si_ptr_pair *data; /**< Pointer to array or pairs of data. */ }; /** Inits hash_si_ptr structure. * @param h pointer to hash_si_ptr struct. * @param size initial size of the hash array. * @return 0 on success, 1 else. */ int hash_si_ptr_init(struct hash_si_ptr *h, size_t size); /** Frees hash_si_ptr structure. * Doesn't call free(h). * @param h pointer to hash_si_ptr struct. */ void hash_si_ptr_deinit(struct hash_si_ptr *h); /** Inserts value into hash_si_ptr. * @param h Pointer to hash_si_ptr struct. * @param key Pointer to key. * @param key_len Key length. * @param value Value. * @return 0 on success, 1 or 2 else. */ /* int hash_si_ptr_insert (struct hash_si_ptr *h, const zend_uintptr_t key, uint32_t value); */ /** Finds value from hash_si_ptr. * Value returned through value param. * @param h Pointer to hash_si_ptr struct. * @param key Pointer to key. * @param key_len Key length. * @param[out] value Found value. * @return 0 if found, 1 if not. */ /* int hash_si_ptr_find (struct hash_si_ptr *h, const zend_uintptr_t key, uint32_t * value); */ /** Returns size of hash_si_ptr. * @param h Pointer to hash_si_ptr struct. * @return Size of hash_si_ptr. */ static zend_always_inline size_t hash_si_ptr_size(struct hash_si_ptr *h) { return h->used; } /** * If the key does not exist, add a mapping from key to value and returns SIZE_MAX * If the key does exist, return the corresponding value * @param h Pointer to hash_si_ptr struct. * @param key key to look up or add * @param value value to insert if it doesn't already exist * * @return SIZE_MAX or old, unmodified key */ size_t hash_si_ptr_find_or_insert(struct hash_si_ptr *h, const zend_uintptr_t key, uint32_t value); /** Returns capacity of hash_si_ptr. * @param h Pointer to hash_si_ptr struct. * @return Capacity of hash_si_ptr. */ zend_always_inline static size_t hash_si_ptr_capacity(struct hash_si_ptr *h) { return h->size; } #endif /* HASH_PTR_H */ igbinary-3.2.13/src/php7/hash_si.c0000664000175000017500000001145014366750307015763 0ustar tysontyson/* +----------------------------------------------------------------------+ | See COPYING file for further copyright information | +----------------------------------------------------------------------+ | Author: Oleg Grenrus | | See CREDITS for contributors | +----------------------------------------------------------------------+ */ #ifdef PHP_WIN32 # include "ig_win32.h" #endif #include #include #include #include #include "hash.h" #include "zend.h" /* {{{ hash_si_init */ /** * Initializes a hash_si value with the given capacity */ int hash_si_init(struct hash_si *h, uint32_t size) { ZEND_ASSERT((size & (size - 1)) == 0 && size > 0); h->mask = size - 1; h->used = 0; h->data = (struct hash_si_pair *)ecalloc(size, sizeof(struct hash_si_pair)); if (h->data == NULL) { return 1; } return 0; } /* }}} */ /* {{{ hash_si_deinit */ void hash_si_deinit(struct hash_si *h) { struct hash_si_pair *const data = h->data; if (h->used > 0) { size_t i; const size_t mask = h->mask; for (i = 0; i <= mask; i++) { if (data[i].key_zstr != NULL) { zend_string_release(data[i].key_zstr); } } } efree(data); } /* }}} */ /* {{{ get_key_hash */ zend_always_inline static uint32_t get_key_hash(zend_string *key_zstr) { /* Fetch the hash, computing and storing it in key_zstr if it was not computed before. */ uint32_t key_hash = ZSTR_HASH(key_zstr); #if SIZEOF_ZEND_LONG > 4 if (UNEXPECTED(key_hash == 0)) { /* A key_hash of uint32_t(0) would be treated like a gap when inserted. Change the hash used to 1 instead. */ /* uint32_t(ZSTR_HASH) is 0 for 1 in 4 billion - optimized builds may use cmove so there are no branch mispredicts, changing to key_hash >> 32 doesn't speed up benchmark/serialize-stringarray */ return 1; } #endif return key_hash; } /* }}} */ /* {{{ hash_si_rehash */ /** Rehash/resize hash_si. * @param h Pointer to hash_si struct. */ zend_always_inline static void hash_si_rehash(struct hash_si *h) { size_t i; size_t old_size; size_t new_size; size_t new_mask; struct hash_si_pair *old_data; struct hash_si_pair *new_data; /* Allocate a table with double the capacity (the next power of 2). */ ZEND_ASSERT(h != NULL); old_size = h->mask + 1; new_size = old_size * 2; new_mask = new_size - 1; old_data = h->data; new_data = (struct hash_si_pair *)ecalloc(new_size, sizeof(struct hash_si_pair)); h->data = new_data; h->mask = new_mask; /* Copy old entries to new entries */ for (i = 0; i < old_size; i++) { const struct hash_si_pair *old_pair = &old_data[i]; if (old_pair->key_zstr != NULL) { uint32_t hv = old_pair->key_hash & new_mask; /* We already computed the hash, avoid recomputing it. */ /* Do linear probing for the next free slot. */ while (new_data[hv].key_hash != 0) { hv = (hv + 1) & new_mask; } new_data[hv] = old_data[i]; } } /* Free old entries */ efree(old_data); } /* }}} */ /* {{{ hash_si_find_or_insert */ /** * If the string key already exists in the map, return the associated value. * If it doesn't exist, indicate that to the caller. * * This is used in igbinary_serialize to deduplicate strings. */ struct hash_si_result hash_si_find_or_insert(struct hash_si *h, zend_string *key_zstr, uint32_t value) { struct hash_si_result result; struct hash_si_pair *pair; struct hash_si_pair *data; uint32_t key_hash = get_key_hash(key_zstr); size_t mask; uint32_t hv; ZEND_ASSERT(h != NULL); mask = h->mask; hv = key_hash & mask; data = h->data; while (1) { pair = &data[hv]; if (pair->key_hash == 0) { /* This is a brand new key */ pair->key_zstr = key_zstr; pair->key_hash = key_hash; pair->value = value; h->used++; /* The size increased, so check if we need to expand the map */ if (UNEXPECTED(h->mask * 3 / 4 < h->used)) { hash_si_rehash(h); } result.code = hash_si_code_inserted; zend_string_addref(key_zstr); return result; } else if (pair->key_hash == key_hash && EXPECTED(zend_string_equals(pair->key_zstr, key_zstr))) { /* This already exists in the hash map */ result.code = hash_si_code_exists; result.value = pair->value; return result; } /* linear prob */ hv = (hv + 1) & mask; } } /* }}} */ /* {{{ hash_si_traverse */ /* void hash_si_traverse(struct hash_si *h, int (*traverse_function) (const char *key, size_t key_len, uint32_t value)) { size_t i; assert(h != NULL && traverse_function != NULL); for (i = 0; i < h->size; i++) { if (h->data[i].key != NULL && traverse_function(h->data[i].key, h->data[i].key_len, h->data[i].value) != 1) { return; } } } */ /* }}} */ /* * Local variables: * tab-width: 2 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ igbinary-3.2.13/src/php7/hash_si_ptr.c0000664000175000017500000001063714366750307016656 0ustar tysontyson/* +----------------------------------------------------------------------+ | See COPYING file for further copyright information | | This is a specialized hash map mapping uintprt_t to int32_t | +----------------------------------------------------------------------+ | Author: Oleg Grenrus | | Modified by Tyson Andre for fixed size, removing unused functions | | See CREDITS for contributors | +----------------------------------------------------------------------+ */ #include #include #include #include #include #include "hash_ptr.h" #include "zend.h" #include "igbinary_bswap.h" /* This assumes that pointers differ in low addresses rather than high addresses */ inline static uint32_t inline_hash_of_address(zend_uintptr_t ptr) { #if UINTPTR_MAX > UINT32_MAX uint64_t hash = ptr; hash *= 0x5e2d58d8b3bce8d9; // This is a single assembly instruction on recent compilers/platforms hash = bswap_64(hash); return hash; #else uint32_t hash = ptr; hash *= 0x5e2d58d9; // This is a single assembly instruction on recent compilers/platforms hash = bswap_32(hash); return hash; #endif } /* {{{ nextpow2 */ /** Next power of 2. * @param n Integer. * @return next to n power of 2 . */ inline static uint32_t nextpow2(uint32_t n) { uint32_t m = 1; while (m < n) { m = m << 1; } return m; } /* }}} */ /* {{{ hash_si_ptr_init */ /** * @param h the pointer to the hash map that will be initialized in place * @param size the new capacity of the hash map */ int hash_si_ptr_init(struct hash_si_ptr *h, size_t size) { size = nextpow2(size); h->size = size; h->used = 0; /* Set everything to 0. sets keys to HASH_PTR_KEY_INVALID. */ h->data = (struct hash_si_ptr_pair *)ecalloc(size, sizeof(struct hash_si_ptr_pair)); if (h->data == NULL) { return 1; } return 0; } /* }}} */ /* {{{ hash_si_ptr_deinit */ /** * Frees the hash map h * @param h Pointer to the hash map (hash_si_ptr struct) to free internal data structures of */ void hash_si_ptr_deinit(struct hash_si_ptr *h) { efree(h->data); } /* }}} */ /* {{{ hash_si_ptr_rehash */ /** Rehash/resize hash_si_ptr. * @param h Pointer to hash_si_ptr struct. */ inline static void hash_si_ptr_rehash(struct hash_si_ptr *h) { size_t i; size_t old_size; size_t size; size_t mask; struct hash_si_ptr_pair *old_data; struct hash_si_ptr_pair *new_data; ZEND_ASSERT(h != NULL); /* Allocate a table with double the capacity (the next power of 2). */ old_size = h->size; size = old_size * 2; mask = size - 1; old_data = h->data; new_data = (struct hash_si_ptr_pair *)ecalloc(size, sizeof(struct hash_si_ptr_pair)); h->size = size; h->data = new_data; /* Copy old entries to new entries */ for (i = 0; i < old_size; i++) { if (old_data[i].key != HASH_PTR_KEY_INVALID) { uint32_t hv = inline_hash_of_address(old_data[i].key) & mask; /* Do linear probing for the next free slot. */ while (new_data[hv].key != HASH_PTR_KEY_INVALID) { ZEND_ASSERT(new_data[hv].key != old_data[i].key); hv = (hv + 1) & mask; } new_data[hv] = old_data[i]; } } /* Free old entries */ efree(old_data); } /* }}} */ /* {{{ hash_si_ptr_find_or_insert */ /** * @param h the pointer to the hash map. * @param key the key (representing to look up (or insert, if it doesn't exist) * @param value - If the key does not exist, this is the value to associate with key * @return the old value, or SIZE_MAX if the key is brand new. */ size_t hash_si_ptr_find_or_insert(struct hash_si_ptr *h, const zend_uintptr_t key, uint32_t value) { size_t size; size_t mask; uint32_t hv; ZEND_ASSERT(h != NULL); size = h->size; mask = size - 1; hv = inline_hash_of_address(key) & mask; while (1) { if (h->data[hv].key == HASH_PTR_KEY_INVALID) { /* This is a brand new key */ h->data[hv].key = key; h->data[hv].value = value; h->used++; /* The size increased, so check if we need to expand the map */ if (UNEXPECTED((h->size >> 1) < h->used)) { hash_si_ptr_rehash(h); } return SIZE_MAX; } else if (h->data[hv].key == key) { /* This already exists in the hash map */ return h->data[hv].value; } /* linear prob */ hv = (hv + 1) & mask; } } /* }}} */ /* * Local variables: * tab-width: 2 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ igbinary-3.2.13/src/php7/igbinary.c0000664000175000017500000035743414366750307016170 0ustar tysontyson/* +----------------------------------------------------------------------+ | See COPYING file for further copyright information | +----------------------------------------------------------------------+ | Author: Oleg Grenrus | | See CREDITS for contributors | +----------------------------------------------------------------------+ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef PHP_WIN32 # include "ig_win32.h" #endif #include "php.h" #include "php_ini.h" #include "Zend/zend_alloc.h" #include "Zend/zend_exceptions.h" #include "Zend/zend_interfaces.h" #include "Zend/zend_compile.h" /* ZEND_ACC_NOT_SERIALIZABLE */ #include "ext/standard/info.h" #include "ext/standard/php_var.h" #if PHP_VERSION_ID >= 80100 #include "Zend/zend_enum.h" #endif #if HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION) # include "ext/session/php_session.h" #endif #include "ext/standard/php_incomplete_class.h" #if PHP_VERSION_ID < 70400 #define zend_get_properties_for(struc, purpose) Z_OBJPROP_P((struc)) #define zend_release_properties(ht) do {} while (0) #endif #if PHP_VERSION_ID < 70300 #define zend_string_efree(s) zend_string_release((s)) #define GC_ADDREF(p) (++GC_REFCOUNT((p))) #endif #if defined(HAVE_APCU_SUPPORT) # include "ext/apcu/apc_serializer.h" #endif /* HAVE_APCU_SUPPORT */ #include "php_igbinary.h" #include "igbinary.h" #include "igbinary_macros.h" #include #include #ifndef PHP_WIN32 # include # include # include #endif #include #include "hash.h" #include "hash_ptr.h" #include "zend_alloc.h" #include "igbinary_zend_hash.h" #if HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION) /** Session serializer function prototypes. */ PS_SERIALIZER_FUNCS(igbinary); #endif /* HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION) */ #if defined(HAVE_APCU_SUPPORT) /** Apc serializer function prototypes */ static int APC_SERIALIZER_NAME(igbinary) (APC_SERIALIZER_ARGS); static int APC_UNSERIALIZER_NAME(igbinary) (APC_UNSERIALIZER_ARGS); #endif static zend_always_inline HashTable *HASH_OF_OBJECT(zval *p) { ZEND_ASSERT(Z_TYPE_P(p) == IS_OBJECT); return Z_OBJ_HT_P(p)->get_properties( #if PHP_VERSION_ID >= 80000 Z_OBJ_P(p) #else p #endif ); } #if PHP_VERSION_ID < 70300 #define zend_string_release_ex(s, persistent) zend_string_release((s)) static zend_always_inline void zval_ptr_dtor_str(zval *zval_ptr) { if (Z_REFCOUNTED_P(zval_ptr) && !Z_DELREF_P(zval_ptr)) { ZEND_ASSERT(Z_TYPE_P(zval_ptr) == IS_STRING); ZEND_ASSERT(!ZSTR_IS_INTERNED(Z_STR_P(zval_ptr))); ZEND_ASSERT(!(GC_FLAGS(Z_STR_P(zval_ptr)) & IS_STR_PERSISTENT)); efree(Z_STR_P(zval_ptr)); } } #endif #define RETURN_1_IF_NON_ZERO(cmd) \ if (UNEXPECTED((cmd) != 0)) { \ return 1; \ } #ifdef ZEND_ACC_NOT_SERIALIZABLE # define IGBINARY_IS_NOT_SERIALIZABLE(ce) UNEXPECTED((ce)->ce_flags & (ZEND_ACC_NOT_SERIALIZABLE | ZEND_ACC_ANON_CLASS)) # define IGBINARY_IS_NOT_UNSERIALIZABLE(ce) IGBINARY_IS_NOT_SERIALIZABLE(ce) #elif PHP_VERSION_ID >= 70400 # define IGBINARY_IS_NOT_SERIALIZABLE(ce) UNEXPECTED((ce)->serialize == zend_class_serialize_deny) # define IGBINARY_IS_NOT_UNSERIALIZABLE(ce) UNEXPECTED((ce)->unserialize == zend_class_unserialize_deny) #else // Because '__serialize' is not available prior to 7.4, this check is redundant. # define IGBINARY_IS_NOT_SERIALIZABLE(ce) (0) # define IGBINARY_IS_NOT_UNSERIALIZABLE(ce) (0) #endif /* {{{ Types */ enum igbinary_type { /* 00 */ igbinary_type_null, /**< Null. */ /* 01 */ igbinary_type_ref8, /**< Array reference. */ /* 02 */ igbinary_type_ref16, /**< Array reference. */ /* 03 */ igbinary_type_ref32, /**< Array reference. */ /* 04 */ igbinary_type_bool_false, /**< Boolean true. */ /* 05 */ igbinary_type_bool_true, /**< Boolean false. */ /* 06 */ igbinary_type_long8p, /**< Long 8bit positive. */ /* 07 */ igbinary_type_long8n, /**< Long 8bit negative. */ /* 08 */ igbinary_type_long16p, /**< Long 16bit positive. */ /* 09 */ igbinary_type_long16n, /**< Long 16bit negative. */ /* 0a */ igbinary_type_long32p, /**< Long 32bit positive. */ /* 0b */ igbinary_type_long32n, /**< Long 32bit negative. */ /* 0c */ igbinary_type_double, /**< Double. */ /* 0d */ igbinary_type_string_empty, /**< Empty string. */ /* 0e */ igbinary_type_string_id8, /**< String id. */ /* 0f */ igbinary_type_string_id16, /**< String id. */ /* 10 */ igbinary_type_string_id32, /**< String id. */ /* 11 */ igbinary_type_string8, /**< String. */ /* 12 */ igbinary_type_string16, /**< String. */ /* 13 */ igbinary_type_string32, /**< String. */ /* 14 */ igbinary_type_array8, /**< Array. */ /* 15 */ igbinary_type_array16, /**< Array. */ /* 16 */ igbinary_type_array32, /**< Array. */ /* 17 */ igbinary_type_object8, /**< Object. */ /* 18 */ igbinary_type_object16, /**< Object. */ /* 19 */ igbinary_type_object32, /**< Object. */ /* 1a */ igbinary_type_object_id8, /**< Object class name string id. */ /* 1b */ igbinary_type_object_id16, /**< Object class name string id. */ /* 1c */ igbinary_type_object_id32, /**< Object class name string id. */ /* 1d */ igbinary_type_object_ser8, /**< Object serialized data. */ /* 1e */ igbinary_type_object_ser16, /**< Object serialized data. */ /* 1f */ igbinary_type_object_ser32, /**< Object serialized data. */ /* 20 */ igbinary_type_long64p, /**< Long 64bit positive. */ /* 21 */ igbinary_type_long64n, /**< Long 64bit negative. */ /* 22 */ igbinary_type_objref8, /**< Object reference. */ /* 23 */ igbinary_type_objref16, /**< Object reference. */ /* 24 */ igbinary_type_objref32, /**< Object reference. */ /* 25 */ igbinary_type_ref, /**< Simple reference */ /* 26 */ igbinary_type_string64, /**< String larger than 4GB (originally, php strings had a limit of 32-bit lengths). */ /* 27 */ igbinary_type_enum_case, /**< PHP 8.1 Enum case. */ }; /* Defers calls to zval_ptr_dtor for values that are refcounted. */ struct deferred_dtor_tracker { zval *zvals; /**< refcounted objects and arrays to call dtor on after unserializing. See i_zval_ptr_dtor */ size_t count; /**< count of refcounted in array for calls to dtor */ size_t capacity; /**< capacity of refcounted in array for calls to dtor */ }; /** Serializer data. * @author Oleg Grenrus */ struct igbinary_serialize_data { uint8_t *buffer; /**< Buffer. */ size_t buffer_size; /**< Buffer size. */ size_t buffer_capacity; /**< Buffer capacity. */ bool scalar; /**< Serializing scalar. */ bool compact_strings; /**< Check for duplicate strings. */ struct hash_si strings; /**< Hash of already serialized strings. */ struct hash_si_ptr references; /**< Hash of already serialized potential references. (non-NULL uintptr_t => int32_t) */ uint32_t references_id; /**< Number of things that the unserializer might think are references. >= length of references */ uint32_t string_count; /**< Serialized string count, used for back referencing */ struct deferred_dtor_tracker deferred_dtor_tracker; /**< refcounted objects and arrays to call dtor on after serializing. See i_zval_ptr_dtor */ }; /* Object { reference {scalar, object, array, null} (convert to reference, share reference in zval_ref) object {} (convert to zend_object, share zend_object* in reference) array {} (convert to zend_array, share zend_array* in reference) empty array {} (use ZVAL_EMPTY_ARRAY to create zvals) } */ enum zval_ref_type { IG_REF_IS_REFERENCE, // Points to zend_reference IG_REF_IS_OBJECT, // Points to zend_object IG_REF_IS_ARRAY, // Points to zend_array #if PHP_VERSION_ID >= 70300 IG_REF_IS_EMPTY_ARRAY, // Use the macro ZVAL_EMPTY_ARRAY to create a pointer to the empty array with the correct type info flags. #endif }; struct igbinary_value_ref { // We reuse temporary values for object properties that are references or arrays. union { zend_reference *reference; zend_object *object; zend_array *array; } reference; enum zval_ref_type type; }; struct deferred_unserialize_call { zval param; /* The array parameter passed to the __unserialize call */ zend_object *object; /* The object which has a deferred call to __unserialize that is going to get called. */ }; struct deferred_call { union { zend_object *wakeup; #if PHP_VERSION_ID >= 70400 /* Currently, zvals are safe to relocate */ struct deferred_unserialize_call unserialize; #endif } data; #if PHP_VERSION_ID >= 70400 zend_bool is_unserialize; #endif }; /** Unserializer data. * @author Oleg Grenrus */ struct igbinary_unserialize_data { const uint8_t *buffer; /**< Buffer with bytes to unserialize. */ const uint8_t *buffer_end; /**< Buffer size. */ const uint8_t *buffer_ptr; /**< Current read offset. */ zend_string **strings; /**< Unserialized strings. */ size_t strings_count; /**< Unserialized string count. */ size_t strings_capacity; /**< Unserialized string array capacity. */ struct igbinary_value_ref *references; /**< Unserialized Arrays/Objects/References */ size_t references_count; /**< Unserialized array/objects count. */ size_t references_capacity; /**< Unserialized array/object array capacity. */ struct deferred_call *deferred; /**< objects&data for calls to __unserialize/__wakeup */ size_t deferred_capacity; /**< capacity of objects in array for calls to __unserialize/__wakeup */ uint32_t deferred_count; /**< count of objects in array for calls to __unserialize/__wakeup. NOTE: Current php releases including 8.1 limit the total number of objects to a 32-bit integer. */ zend_bool deferred_finished; /**< whether the deferred calls were performed */ struct deferred_dtor_tracker deferred_dtor_tracker; /**< refcounted objects and arrays to call dtor on after unserializing. See i_zval_ptr_dtor */ #if PHP_VERSION_ID >= 70400 HashTable *ref_props; /**< objects&data for calls to __unserialize/__wakeup */ #endif }; #define IGB_REF_VAL_2(igsd, n) ((igsd)->references[(n)]) #define IGB_NEEDS_MORE_DATA(igsd, n) UNEXPECTED((size_t)((igsd)->buffer_end - (igsd)->buffer_ptr) < (n)) #define IGB_REMAINING_BYTES(igsd) ((unsigned int)((igsd)->buffer_end - (igsd)->buffer_ptr)) #define IGB_BUFFER_OFFSET(igsd) ((unsigned int)((igsd)->buffer_ptr - (igsd)->buffer)) #define WANT_CLEAR (0) #define WANT_REF (1 << 1) /* }}} */ /* {{{ Serializing functions prototypes */ zend_always_inline static int igbinary_serialize_data_init(struct igbinary_serialize_data *igsd, bool scalar); zend_always_inline static void igbinary_serialize_data_deinit(struct igbinary_serialize_data *igsd); zend_always_inline static void igbinary_serialize_header(struct igbinary_serialize_data *igsd); zend_always_inline static int igbinary_serialize8(struct igbinary_serialize_data *igsd, uint8_t i); zend_always_inline static int igbinary_serialize16(struct igbinary_serialize_data *igsd, uint16_t i); zend_always_inline static int igbinary_serialize32(struct igbinary_serialize_data *igsd, uint32_t i); zend_always_inline static int igbinary_serialize64(struct igbinary_serialize_data *igsd, uint64_t i); zend_always_inline static int igbinary_serialize_null(struct igbinary_serialize_data *igsd); zend_always_inline static int igbinary_serialize_bool(struct igbinary_serialize_data *igsd, int b); zend_always_inline static int igbinary_serialize_long(struct igbinary_serialize_data *igsd, zend_long l); zend_always_inline static int igbinary_serialize_double(struct igbinary_serialize_data *igsd, double d); zend_always_inline static int igbinary_serialize_string(struct igbinary_serialize_data *igsd, zend_string *s); zend_always_inline static int igbinary_serialize_chararray(struct igbinary_serialize_data *igsd, const char *s, size_t len); zend_always_inline static int igbinary_serialize_array(struct igbinary_serialize_data *igsd, zval *z, bool object, bool incomplete_class, bool serialize_props); zend_always_inline static int igbinary_serialize_array_ref(struct igbinary_serialize_data *igsd, zval *z, bool object); zend_always_inline static int igbinary_serialize_array_sleep(struct igbinary_serialize_data *igsd, zval *z, HashTable *ht, zend_class_entry *ce); zend_always_inline static int igbinary_serialize_object_name(struct igbinary_serialize_data *igsd, zend_string *name); zend_always_inline static int igbinary_serialize_object(struct igbinary_serialize_data *igsd, zval *z); static int igbinary_serialize_zval(struct igbinary_serialize_data *igsd, zval *z); /* }}} */ /* {{{ Unserializing functions prototypes */ zend_always_inline static int igbinary_unserialize_data_init(struct igbinary_unserialize_data *igsd); zend_always_inline static void igbinary_unserialize_data_deinit(struct igbinary_unserialize_data *igsd); zend_always_inline static int igbinary_unserialize_header(struct igbinary_unserialize_data *igsd); zend_always_inline static uint8_t igbinary_unserialize8(struct igbinary_unserialize_data *igsd); zend_always_inline static uint16_t igbinary_unserialize16(struct igbinary_unserialize_data *igsd); zend_always_inline static uint32_t igbinary_unserialize32(struct igbinary_unserialize_data *igsd); zend_always_inline static uint64_t igbinary_unserialize64(struct igbinary_unserialize_data *igsd); zend_always_inline static int igbinary_unserialize_long(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zend_long *ret); zend_always_inline static int igbinary_unserialize_double(struct igbinary_unserialize_data *igsd, double *ret); zend_always_inline static zend_string *igbinary_unserialize_string(struct igbinary_unserialize_data *igsd, enum igbinary_type t); zend_always_inline static zend_string *igbinary_unserialize_chararray(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zend_bool check_interned); zend_always_inline static int igbinary_unserialize_array(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval *const z, int flags, zend_bool create_ref); zend_always_inline static int igbinary_unserialize_object(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval *const z, int flags); static int igbinary_unserialize_object_ser(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval *const z, zend_class_entry *ce); static int igbinary_unserialize_zval(struct igbinary_unserialize_data *igsd, zval *const z, int flags); /* }}} */ /* {{{ arginfo */ ZEND_BEGIN_ARG_INFO_EX(arginfo_igbinary_serialize, 0, 0, 1) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_igbinary_unserialize, 0, 0, 1) ZEND_ARG_INFO(0, str) ZEND_END_ARG_INFO() /* }}} */ /* {{{ igbinary_functions[] */ /** Exported php functions. */ zend_function_entry igbinary_functions[] = { PHP_FE(igbinary_serialize, arginfo_igbinary_serialize) PHP_FE(igbinary_unserialize, arginfo_igbinary_unserialize) PHP_FE_END }; /* }}} */ /* {{{ igbinary dependencies */ static const zend_module_dep igbinary_module_deps[] = { ZEND_MOD_REQUIRED("standard") #if HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION) ZEND_MOD_REQUIRED("session") #endif #if defined(HAVE_APCU_SUPPORT) ZEND_MOD_OPTIONAL("apcu") #endif ZEND_MOD_END }; /* }}} */ /* {{{ igbinary_module_entry */ zend_module_entry igbinary_module_entry = { STANDARD_MODULE_HEADER_EX, NULL, igbinary_module_deps, "igbinary", igbinary_functions, PHP_MINIT(igbinary), PHP_MSHUTDOWN(igbinary), NULL, NULL, PHP_MINFO(igbinary), PHP_IGBINARY_VERSION, STANDARD_MODULE_PROPERTIES }; /* }}} */ ZEND_DECLARE_MODULE_GLOBALS(igbinary) /* {{{ ZEND_GET_MODULE */ #ifdef COMPILE_DL_IGBINARY ZEND_GET_MODULE(igbinary) #endif /* }}} */ /* {{{ INI entries */ PHP_INI_BEGIN() STD_PHP_INI_BOOLEAN("igbinary.compact_strings", "1", PHP_INI_ALL, OnUpdateBool, compact_strings, zend_igbinary_globals, igbinary_globals) PHP_INI_END() /* }}} */ /* {{{ php_igbinary_init_globals */ static void php_igbinary_init_globals(zend_igbinary_globals *igbinary_globals) { igbinary_globals->compact_strings = 1; } /* }}} */ /* {{{ PHP_MINIT_FUNCTION */ /** * The module init function. * This allocates the persistent resources of this PHP module. */ PHP_MINIT_FUNCTION(igbinary) { (void)type; (void)module_number; ZEND_INIT_MODULE_GLOBALS(igbinary, php_igbinary_init_globals, NULL); #if HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION) php_session_register_serializer("igbinary", PS_SERIALIZER_ENCODE_NAME(igbinary), PS_SERIALIZER_DECODE_NAME(igbinary)); #endif #if defined(HAVE_APCU_SUPPORT) apc_register_serializer("igbinary", APC_SERIALIZER_NAME(igbinary), APC_UNSERIALIZER_NAME(igbinary), NULL); #endif REGISTER_INI_ENTRIES(); return SUCCESS; } /* }}} */ /* {{{ PHP_MSHUTDOWN_FUNCTION */ /** * The module shutdown function. * This cleans up all persistent resources of this PHP module. */ PHP_MSHUTDOWN_FUNCTION(igbinary) { (void)type; (void)module_number; #ifdef ZTS ts_free_id(igbinary_globals_id); #endif /* * Clean up ini entries. * Aside: It seems like the php_session_register_serializer unserializes itself, since MSHUTDOWN in ext/wddx/wddx.c doesn't exist? */ UNREGISTER_INI_ENTRIES(); return SUCCESS; } /* }}} */ /* {{{ PHP_MINFO_FUNCTION */ PHP_MINFO_FUNCTION(igbinary) { (void)zend_module; php_info_print_table_start(); php_info_print_table_row(2, "igbinary support", "enabled"); php_info_print_table_row(2, "igbinary version", PHP_IGBINARY_VERSION); #if defined(HAVE_APCU_SUPPORT) php_info_print_table_row(2, "igbinary APCu serializer ABI", APC_SERIALIZER_ABI); #else php_info_print_table_row(2, "igbinary APCu serializer ABI", "no"); #endif #if HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION) php_info_print_table_row(2, "igbinary session support", "yes"); #else php_info_print_table_row(2, "igbinary session support", "no"); #endif php_info_print_table_end(); DISPLAY_INI_ENTRIES(); } /* }}} */ /* {{{ igsd management */ /* Append to list of references to take out later. Returns SIZE_MAX on allocation error. */ static zend_always_inline size_t igsd_append_ref(struct igbinary_unserialize_data *igsd, struct igbinary_value_ref v) { size_t ref_n; if (igsd->references_count + 1 >= igsd->references_capacity) { igsd->references_capacity *= 2; struct igbinary_value_ref *new_references = erealloc(igsd->references, sizeof(igsd->references[0]) * igsd->references_capacity); if (UNEXPECTED(new_references == NULL)) { return SIZE_MAX; } igsd->references = new_references; } ref_n = igsd->references_count++; IGB_REF_VAL_2(igsd, ref_n) = v; return ref_n; } static zend_always_inline int igsd_ensure_defer_capacity(struct igbinary_unserialize_data *igsd) { if (igsd->deferred_count >= igsd->deferred_capacity) { if (igsd->deferred_capacity == 0) { igsd->deferred_capacity = 2; igsd->deferred = emalloc(sizeof(igsd->deferred[0]) * igsd->deferred_capacity); } else { igsd->deferred_capacity *= 2; struct deferred_call *old_deferred = igsd->deferred; igsd->deferred = erealloc(old_deferred, sizeof(igsd->deferred[0]) * igsd->deferred_capacity); if (UNEXPECTED(igsd->deferred == NULL)) { igsd->deferred = old_deferred; return 1; } } } return 0; } static inline int igsd_defer_wakeup(struct igbinary_unserialize_data *igsd, zend_object *object) { // TODO: This won't be properly garbage collected if there is an OOM error, but would php terminate instead? RETURN_1_IF_NON_ZERO(igsd_ensure_defer_capacity(igsd)); struct deferred_call *c = &igsd->deferred[igsd->deferred_count++]; c->data.wakeup = object; #if PHP_VERSION_ID >= 70400 c->is_unserialize = 0; #endif return 0; } /* igsd_defer_unserialize {{{ */ #if PHP_VERSION_ID >= 70400 static inline int igsd_defer_unserialize(struct igbinary_unserialize_data *igsd, zend_object *object, zval param) { RETURN_1_IF_NON_ZERO(igsd_ensure_defer_capacity(igsd)); struct deferred_call *c = &igsd->deferred[igsd->deferred_count++]; struct deferred_unserialize_call* call = &c->data.unserialize; call->object = object; ZEND_ASSERT(Z_TYPE(param) == IS_ARRAY); call->param = param; c->is_unserialize = 1; return 0; } #endif /* }}} */ /* {{{ igbinary_finish_deferred_calls * After all object instances were unserialized, perform the deferred calls to __wakeup() on all of the objects implementing that method. */ static int igbinary_finish_deferred_calls(struct igbinary_unserialize_data *igsd) { #if PHP_VERSION_ID >= 70400 && PHP_VERSION_ID < 80000 zval unserialize_name; #endif zval wakeup_name; uint32_t i; struct deferred_call *deferred_arr; uint32_t deferred_count = igsd->deferred_count; zend_bool delayed_call_failed = 0; igsd->deferred_finished = 1; if (deferred_count == 0) { /* nothing to do */ return 0; } deferred_arr = igsd->deferred; #if PHP_VERSION_ID >= 70400 && PHP_VERSION_ID < 80000 ZVAL_STRINGL(&unserialize_name, "__unserialize", sizeof("__unserialize") - 1); #endif ZVAL_STRINGL(&wakeup_name, "__wakeup", sizeof("__wakeup") - 1); for (i = 0; i < deferred_count; i++) { struct deferred_call *deferred = &deferred_arr[i]; #if PHP_VERSION_ID >= 70400 if (deferred->is_unserialize) { struct deferred_unserialize_call *unserialize_call = &deferred->data.unserialize; zend_object *const obj = unserialize_call->object; ZEND_ASSERT(Z_TYPE(unserialize_call->param) == IS_ARRAY); if (!delayed_call_failed) { #if PHP_VERSION_ID >= 80000 /* Copy the parameter for __unserialize so that changes in __unserialize won't mutate the original. */ // ZVAL_COPY(¶m, &unserialize_call->param); BG(serialize_lock)++; zend_call_known_instance_method_with_1_params( obj->ce->__unserialize, obj, NULL, &unserialize_call->param); if (EG(exception)) { delayed_call_failed = 1; GC_ADD_FLAGS(obj, IS_OBJ_DESTRUCTOR_CALLED); } BG(serialize_lock)--; #else zval retval; zval zv; ZVAL_OBJ(&zv, obj); /* Copy the parameter for __unserialize so that changes in __unserialize won't mutate the original. */ // ZVAL_COPY(¶m, &unserialize_call->param); BG(serialize_lock)++; if (call_user_function(CG(function_table), &zv, &unserialize_name, &retval, 1, &unserialize_call->param) == FAILURE || Z_ISUNDEF(retval)) { delayed_call_failed = 1; GC_ADD_FLAGS(obj, IS_OBJ_DESTRUCTOR_CALLED); } BG(serialize_lock)--; zval_ptr_dtor(&retval); #endif } else { GC_ADD_FLAGS(obj, IS_OBJ_DESTRUCTOR_CALLED); } zval_ptr_dtor(&unserialize_call->param); } else #endif { zend_object *obj = deferred->data.wakeup; if (!delayed_call_failed) { zval retval; /* return value of __wakeup */ zval rval; ZVAL_OBJ(&rval, obj); if (UNEXPECTED(call_user_function(CG(function_table), &rval, &wakeup_name, &retval, 0, 0) == FAILURE || Z_ISUNDEF(retval))) { delayed_call_failed = 1; GC_ADD_FLAGS(obj, IS_OBJ_DESTRUCTOR_CALLED); } zval_ptr_dtor(&retval); } else { GC_ADD_FLAGS(obj, IS_OBJ_DESTRUCTOR_CALLED); } } } zval_ptr_dtor_str(&wakeup_name); #if PHP_VERSION_ID >= 70400 && PHP_VERSION_ID < 80000 zval_ptr_dtor_str(&unserialize_name); #endif return delayed_call_failed; } /* }}} */ /* }}} */ /* {{{ igsd_ensure_deferred_dtor_capacity(struct igbinary_serialize_data *igsd) */ static inline int igsd_ensure_deferred_dtor_capacity(struct deferred_dtor_tracker *tracker) { if (tracker->count >= tracker->capacity) { if (tracker->capacity == 0) { tracker->capacity = 2; tracker->zvals = emalloc(sizeof(tracker->zvals[0]) * tracker->capacity); } else { tracker->capacity *= 2; zval *old_deferred_dtor = tracker->zvals; tracker->zvals = erealloc(old_deferred_dtor, sizeof(tracker->zvals[0]) * tracker->capacity); if (UNEXPECTED(tracker->zvals == NULL)) { tracker->zvals = old_deferred_dtor; return 1; } } } return 0; } /* }}} */ /* {{{ free_deferred_dtors(struct deferred_dtor_tracker *tracker) */ static zend_always_inline void free_deferred_dtors(struct deferred_dtor_tracker *tracker) { zval *const zvals = tracker->zvals; if (zvals) { const size_t n = tracker->count; size_t i; for (i = 0; i < n; i++) { /* fprintf(stderr, "freeing i=%d id=%d refcount=%d\n", (int)i, (int)Z_OBJ_HANDLE(zvals[i]), (int)Z_REFCOUNT(zvals[i])); */ zval_ptr_dtor(&zvals[i]); } efree(zvals); } } /* }}} */ /* {{{ igsd_addref_and_defer_dtor(struct igbinary_serialize_data *igsd, zval *z) */ static zend_always_inline int igsd_addref_and_defer_dtor(struct deferred_dtor_tracker *tracker, zval *z) { if (!Z_REFCOUNTED_P(z)) { return 0; } if (UNEXPECTED(igsd_ensure_deferred_dtor_capacity(tracker))) { return 1; } ZEND_ASSERT(Z_REFCOUNT_P(z) >= 1); /* Expect that there were references at the time this was serialized */ ZVAL_COPY(&tracker->zvals[tracker->count++], z); /* Copy and increase reference count */ return 0; } /* }}} */ /* {{{ igsd_defer_dtor(struct igbinary_serialize_data *igsd, zval *z) */ static inline int igsd_defer_dtor(struct deferred_dtor_tracker *tracker, zval *z) { if (!Z_REFCOUNTED_P(z)) { return 0; } if (igsd_ensure_deferred_dtor_capacity(tracker)) { return 1; } ZEND_ASSERT(Z_REFCOUNT_P(z) >= 1); /* Expect that there were references at the time this was serialized */ ZVAL_COPY_VALUE(&tracker->zvals[tracker->count++], z); /* Copy without increasing reference count */ return 0; } /* }}} */ /* {{{ int igbinary_serialize(uint8_t**, size_t*, zval*) */ IGBINARY_API int igbinary_serialize(uint8_t **ret, size_t *ret_len, zval *z) { return igbinary_serialize_ex(ret, ret_len, z, NULL); } /* }}} */ /* {{{ int igbinary_serialize_ex(uint8_t**, size_t*, zval*, igbinary_memory_manager*) */ /** * Serializes data, and writes the allocated byte buffer into ret and the buffer's length into ret_len. * @param ret output parameter * @param ret_len length of byte buffer ret * @param z the zval (data) to serialize * @param memory_manager (nullable) the memory manager to use for allocating/reallocating the buffer of serialized data. Used by extensions such as APCu * @return 0 on success, 1 on failure */ IGBINARY_API int igbinary_serialize_ex(uint8_t **ret, size_t *ret_len, zval *z, struct igbinary_memory_manager *memory_manager) { struct igbinary_serialize_data igsd; uint8_t *tmpbuf; int return_code; // While we can't get passed references through the PHP_FUNCTIONs igbinary declares, third party code can invoke igbinary's methods with references. // See https://github.com/php-memcached-dev/php-memcached/issues/326 if (UNEXPECTED(Z_TYPE_P(z) == IS_INDIRECT)) { z = Z_INDIRECT_P(z); } ZVAL_DEREF(z); if (UNEXPECTED(igbinary_serialize_data_init(&igsd, Z_TYPE_P(z) != IS_OBJECT && Z_TYPE_P(z) != IS_ARRAY))) { zend_error(E_WARNING, "igbinary_serialize: cannot init igsd"); return 1; } igbinary_serialize_header(&igsd); return_code = 0; if (UNEXPECTED(igbinary_serialize_zval(&igsd, z) != 0)) { return_code = 1; goto cleanup; } /* Explicit null termination */ /* TODO: Stop doing this in the next major version, serialized data can contain nulls in the middle and callers should check length */ if (UNEXPECTED(igbinary_serialize8(&igsd, 0) != 0)) { return_code = 1; goto cleanup; } /* shrink buffer to the real length, ignore errors */ if (UNEXPECTED(memory_manager)) { tmpbuf = memory_manager->alloc(igsd.buffer_size, memory_manager->context); if (tmpbuf != NULL) { memcpy(tmpbuf, igsd.buffer, igsd.buffer_size); } if (tmpbuf == NULL) { return_code = 1; goto cleanup; } *ret = tmpbuf; *ret_len = igsd.buffer_size - 1; } else { /* Set return values */ *ret_len = igsd.buffer_size - 1; *ret = igsd.buffer; igsd.buffer = NULL; } cleanup: igbinary_serialize_data_deinit(&igsd); return return_code; } /* }}} */ /* {{{ int igbinary_unserialize(const uint8_t *, size_t, zval **) */ /** * Unserializes the data into z * @param buf the read-only buffer with the serialized data * @param buf_len the length of that buffer. * @param z output parameter. Will contain the unserialized value(zval). * @return 0 on success, 1 on failure */ IGBINARY_API int igbinary_unserialize(const uint8_t *buf, size_t buf_len, zval *z) { struct igbinary_unserialize_data igsd; int ret = 0; igbinary_unserialize_data_init(&igsd); igsd.buffer = buf; igsd.buffer_ptr = buf; igsd.buffer_end = buf + buf_len; if (UNEXPECTED(igbinary_unserialize_header(&igsd))) { ret = 1; goto cleanup; } if (UNEXPECTED(igbinary_unserialize_zval(&igsd, z, WANT_CLEAR))) { ret = 1; goto cleanup; } if (Z_REFCOUNTED_P(z)) { #if PHP_VERSION_ID >= 70200 zend_refcounted *ref = Z_COUNTED_P(z); gc_check_possible_root(ref); #else gc_check_possible_root(z); #endif } if (UNEXPECTED(igsd.buffer_ptr < igsd.buffer_end)) { // https://github.com/igbinary/igbinary/issues/64 zend_error(E_WARNING, "igbinary_unserialize: received more data to unserialize than expected"); ret = 1; goto cleanup; } if (UNEXPECTED(igbinary_finish_deferred_calls(&igsd))) { ret = 1; goto cleanup; } cleanup: igbinary_unserialize_data_deinit(&igsd); return ret; } /* }}} */ /* {{{ proto string igbinary_unserialize(mixed value) */ /** * @see igbinary.php for more detailed API documentation. */ PHP_FUNCTION(igbinary_unserialize) { char *string = NULL; size_t string_len; if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &string, &string_len) == FAILURE) { RETURN_NULL(); } if (string_len <= 0) { RETURN_FALSE; } if (igbinary_unserialize((uint8_t *)string, string_len, return_value) != 0) { /* FIXME: is this a good place? a catch all */ zval_ptr_dtor(return_value); RETURN_NULL(); } } /* }}} */ /* {{{ proto mixed igbinary_serialize(string value) */ /** * @see igbinary.php for more detailed API documentation. */ PHP_FUNCTION(igbinary_serialize) { zval *z; uint8_t *string; size_t string_len; if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &z) == FAILURE) { RETURN_NULL(); } if (igbinary_serialize(&string, &string_len, z) != 0) { RETURN_NULL(); } RETVAL_STRINGL((char *)string, string_len); efree(string); } /* }}} */ #if HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION) /* {{{ Serializer encode function */ /** * This provides a serializer encode function for PHP's session module (using igbinary), * if igbinary was compiled with session support. * * Session support has to be statically compiled into php to use igbinary, * due to the lack of a cross-platform way to register a session serializer/unserializer * when the session module isn't available. */ PS_SERIALIZER_ENCODE_FUNC(igbinary) { zval *session_vars; zend_string *result; struct igbinary_serialize_data igsd; session_vars = &(PS(http_session_vars)); if (Z_ISREF_P(session_vars)) { session_vars = Z_REFVAL_P(session_vars); } if (igbinary_serialize_data_init(&igsd, false)) { zend_error(E_WARNING, "igbinary_serialize: cannot init igsd"); return ZSTR_EMPTY_ALLOC(); } igbinary_serialize_header(&igsd); /** We serialize the passed in array of session_var (including the empty array, for #231) */ /** the same way we would serialize a regular array. */ /** The corresponding PS_SERIALIZER_DECODE_FUNC will unserialize the array and individually add the session variables. */ if (igbinary_serialize_array(&igsd, session_vars, false, false, true) != 0) { zend_error(E_WARNING, "igbinary_serialize: cannot serialize session variables"); result = ZSTR_EMPTY_ALLOC(); } else { /* Copy the buffer to a new zend_string */ result = zend_string_init((const char *)igsd.buffer, igsd.buffer_size, 0); } igbinary_serialize_data_deinit(&igsd); return result; } /* }}} */ /* {{{ Serializer decode function */ /** * This provides a serializer decode function for PHP's session module (using igbinary), * if igbinary was compiled with session support. * * Session support has to be statically compiled into php to use igbinary, * due to the lack of a cross-platform way to register a session serializer/unserializer * when the session module isn't available. * * This is similar to PS_SERIALIZER_DECODE_FUNC(php) from ext/session/session.c */ PS_SERIALIZER_DECODE_FUNC(igbinary) { HashTable *tmp_hash; zval z; zval *d; zend_string *key; int ret = 0; struct igbinary_unserialize_data igsd; if (!val || vallen == 0) { return SUCCESS; } if (igbinary_unserialize_data_init(&igsd) != 0) { return FAILURE; } igsd.buffer = (const uint8_t *)val; igsd.buffer_ptr = igsd.buffer; igsd.buffer_end = igsd.buffer + vallen; if (UNEXPECTED(igbinary_unserialize_header(&igsd))) { ret = 1; goto deinit; } /** The serializer serialized the session variables as an array. So, we unserialize that array. */ /** We then iterate over the array to set the individual session variables (managing the reference counts), then free the original array. */ if (UNEXPECTED(igbinary_unserialize_zval(&igsd, &z, WANT_CLEAR))) { ret = 1; goto deinit; } ret = igbinary_finish_deferred_calls(&igsd); deinit: igbinary_unserialize_data_deinit(&igsd); if (UNEXPECTED(ret)) { return FAILURE; } /* Validate that this is of the correct data type */ tmp_hash = HASH_OF(&z); if (tmp_hash == NULL) { zval_ptr_dtor(&z); return FAILURE; } ZEND_HASH_FOREACH_STR_KEY_VAL(tmp_hash, key, d) { if (key == NULL) { /* array key is a number, how? Skip it. */ /* ??? */ continue; } if (php_set_session_var(key, d, NULL)) { /* Added to session successfully */ /* Refcounted types such as arrays, objects, references need to have references incremented manually, so that zval_ptr_dtor doesn't clean up pointers they include. */ /* Non-refcounted types have the data copied. */ Z_TRY_ADDREF_P(d); } } ZEND_HASH_FOREACH_END(); zval_ptr_dtor(&z); return SUCCESS; } /* }}} */ #endif /* HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION) */ #if defined(HAVE_APCU_SUPPORT) /* {{{ apc_serialize function */ static int APC_SERIALIZER_NAME(igbinary) ( APC_SERIALIZER_ARGS ) { (void)config; if (igbinary_serialize(buf, buf_len, (zval *)value) == 0) { /* flipped semantics - We return 1 to indicate success to APCu (and 0 for failure) */ return 1; } return 0; } /* }}} */ /* {{{ apc_unserialize function */ static int APC_UNSERIALIZER_NAME(igbinary) ( APC_UNSERIALIZER_ARGS ) { (void)config; if (igbinary_unserialize(buf, buf_len, value) == 0) { /* flipped semantics - We return 1 to indicate success to APCu (and 0 for failure) */ return 1; } /* Failed. free return value */ zval_ptr_dtor(value); ZVAL_NULL(value); /* and replace the incomplete value with null just in case APCu uses it in the future */ return 0; } /* }}} */ #endif /* {{{ igbinary_serialize_data_init */ /** * Allocates data structures needed by igbinary_serialize_data, * and sets properties of igsd to their defaults. * @param igsd the struct to initialize * @param scalar true if the data being serialized is a scalar * @param memory_manager optional override of the memory manager */ inline static int igbinary_serialize_data_init(struct igbinary_serialize_data *igsd, bool scalar) { void *buffer = emalloc(32); if (UNEXPECTED(buffer == NULL)) { return 1; } igsd->buffer_size = 0; igsd->buffer_capacity = 32; igsd->string_count = 0; igsd->buffer = buffer; igsd->scalar = scalar; if (scalar) { igsd->compact_strings = 0; } else { hash_si_init(&igsd->strings, 16); hash_si_ptr_init(&igsd->references, 16); igsd->references_id = 0; igsd->compact_strings = (bool)IGBINARY_G(compact_strings); igsd->deferred_dtor_tracker.zvals = NULL; igsd->deferred_dtor_tracker.count = 0; igsd->deferred_dtor_tracker.capacity = 0; } return 0; } /* }}} */ /* {{{ igbinary_serialize_data_deinit */ /** Deinits igbinary_serialize_data, freeing the allocated data structures. */ inline static void igbinary_serialize_data_deinit(struct igbinary_serialize_data *igsd) { bool scalar = igsd->scalar; if (igsd->buffer) { efree(igsd->buffer); } if (!scalar) { hash_si_deinit(&igsd->strings); hash_si_ptr_deinit(&igsd->references); free_deferred_dtors(&igsd->deferred_dtor_tracker); } } /* }}} */ /* {{{ igbinary_serialize_header */ /** Serializes header ("\x00\x00\x00\x02"). */ inline static void igbinary_serialize_header(struct igbinary_serialize_data *igsd) { uint8_t* append_buffer = igsd->buffer; ZEND_ASSERT(igsd->buffer_size == 0); ZEND_ASSERT(igsd->buffer_capacity >= 4); append_buffer[0] = 0; append_buffer[1] = 0; append_buffer[2] = 0; append_buffer[3] = IGBINARY_FORMAT_VERSION; igsd->buffer_size = 4; } /* }}} */ static int igbinary_raise_capacity(struct igbinary_serialize_data *igsd, size_t size) { do { igsd->buffer_capacity *= 2; } while (igsd->buffer_size + size >= igsd->buffer_capacity); uint8_t *const old_buffer = igsd->buffer; igsd->buffer = erealloc(old_buffer, igsd->buffer_capacity); if (UNEXPECTED(igsd->buffer == NULL)) { /* We failed to allocate a larger buffer for the result. Free the memory used for the original buffer. */ efree(old_buffer); return 1; } return 0; } /* {{{ igbinary_serialize_resize */ /** Expands igbinary_serialize_data if necessary. */ zend_always_inline static int igbinary_serialize_resize(struct igbinary_serialize_data *igsd, size_t size) { if (igsd->buffer_size + size < igsd->buffer_capacity) { return 0; } return igbinary_raise_capacity(igsd, size); } /* }}} */ /* {{{ igbinary_serialize8 */ /** Serialize 8bit value. */ zend_always_inline static int igbinary_serialize8(struct igbinary_serialize_data *igsd, uint8_t i) { RETURN_1_IF_NON_ZERO(igbinary_serialize_resize(igsd, 1)); igsd->buffer[igsd->buffer_size++] = i; return 0; } /* }}} */ /* {{{ igbinary_serialize16 */ /** Serialize 16bit value. */ zend_always_inline static int igbinary_serialize16(struct igbinary_serialize_data *igsd, uint16_t i) { uint8_t *append_buffer; RETURN_1_IF_NON_ZERO(igbinary_serialize_resize(igsd, 2)); append_buffer = &igsd->buffer[igsd->buffer_size]; append_buffer[0] = (uint8_t)(i >> 8 & 0xff); append_buffer[1] = (uint8_t)(i & 0xff); igsd->buffer_size += 2; return 0; } /* }}} */ /* {{{ igbinary_serialize32 */ /** Serialize 32bit value. */ zend_always_inline static int igbinary_serialize32(struct igbinary_serialize_data *igsd, uint32_t i) { uint8_t *append_buffer; RETURN_1_IF_NON_ZERO(igbinary_serialize_resize(igsd, 4)); append_buffer = &igsd->buffer[igsd->buffer_size]; append_buffer[0] = (uint8_t)(i >> 24 & 0xff); append_buffer[1] = (uint8_t)(i >> 16 & 0xff); append_buffer[2] = (uint8_t)(i >> 8 & 0xff); append_buffer[3] = (uint8_t)(i & 0xff); igsd->buffer_size += 4; return 0; } /* }}} */ /* {{{ igbinary_serialize64 */ /** Serialize 64bit value. */ zend_always_inline static int igbinary_serialize64(struct igbinary_serialize_data *igsd, uint64_t i) { uint8_t *append_buffer; RETURN_1_IF_NON_ZERO(igbinary_serialize_resize(igsd, 8)); append_buffer = &igsd->buffer[igsd->buffer_size]; append_buffer[0] = (uint8_t)(i >> 56 & 0xff); append_buffer[1] = (uint8_t)(i >> 48 & 0xff); append_buffer[2] = (uint8_t)(i >> 40 & 0xff); append_buffer[3] = (uint8_t)(i >> 32 & 0xff); append_buffer[4] = (uint8_t)(i >> 24 & 0xff); append_buffer[5] = (uint8_t)(i >> 16 & 0xff); append_buffer[6] = (uint8_t)(i >> 8 & 0xff); append_buffer[7] = (uint8_t)(i & 0xff); igsd->buffer_size += 8; return 0; } /* }}} */ /* {{{ igbinary_serialize8 */ /** Serialize 8bit value + 8bit value. */ zend_always_inline static int igbinary_serialize8_and_8(struct igbinary_serialize_data *igsd, uint8_t i, uint8_t v) { uint8_t *append_buffer; RETURN_1_IF_NON_ZERO(igbinary_serialize_resize(igsd, 2)); append_buffer = &igsd->buffer[igsd->buffer_size]; append_buffer[0] = i; append_buffer[1] = v; igsd->buffer_size += 2; return 0; } /* }}} */ /** Serialize 8bit value + 16bit value. */ zend_always_inline static int igbinary_serialize8_and_16(struct igbinary_serialize_data *igsd, uint8_t i, uint16_t v) { uint8_t *append_buffer; RETURN_1_IF_NON_ZERO(igbinary_serialize_resize(igsd, 3)); append_buffer = &igsd->buffer[igsd->buffer_size]; append_buffer[0] = i; append_buffer[1] = (uint8_t)(v >> 8 & 0xff); append_buffer[2] = (uint8_t)(v & 0xff); ; igsd->buffer_size += 3; return 0; } /* }}} */ /** Serialize 8bit value + 32bit value. */ zend_always_inline static int igbinary_serialize8_and_32(struct igbinary_serialize_data *igsd, uint8_t i, uint32_t v) { uint8_t *append_buffer; RETURN_1_IF_NON_ZERO(igbinary_serialize_resize(igsd, 5)); append_buffer = &igsd->buffer[igsd->buffer_size]; append_buffer[0] = i; append_buffer[1] = (uint8_t)(v >> 24 & 0xff); append_buffer[2] = (uint8_t)(v >> 16 & 0xff); append_buffer[3] = (uint8_t)(v >> 8 & 0xff); append_buffer[4] = (uint8_t)(v & 0xff); ; igsd->buffer_size += 5; return 0; } /* }}} */ /** Serialize 8bit value + 64bit value. */ inline static int igbinary_serialize8_and_64(struct igbinary_serialize_data *igsd, uint8_t i, uint64_t v) { uint8_t *append_buffer; RETURN_1_IF_NON_ZERO(igbinary_serialize_resize(igsd, 9)); append_buffer = &igsd->buffer[igsd->buffer_size]; append_buffer[0] = i; append_buffer[1] = (uint8_t)(v >> 56 & 0xff); append_buffer[2] = (uint8_t)(v >> 48 & 0xff); append_buffer[3] = (uint8_t)(v >> 40 & 0xff); append_buffer[4] = (uint8_t)(v >> 32 & 0xff); append_buffer[5] = (uint8_t)(v >> 24 & 0xff); append_buffer[6] = (uint8_t)(v >> 16 & 0xff); append_buffer[7] = (uint8_t)(v >> 8 & 0xff); append_buffer[8] = (uint8_t)(v & 0xff); ; igsd->buffer_size += 9; return 0; } /* }}} */ /* {{{ igbinary_serialize_null */ /** Serializes null. */ inline static int igbinary_serialize_null(struct igbinary_serialize_data *igsd) { return igbinary_serialize8(igsd, igbinary_type_null); } /* }}} */ /* {{{ igbinary_serialize_bool */ /** Serializes bool. */ inline static int igbinary_serialize_bool(struct igbinary_serialize_data *igsd, int b) { return igbinary_serialize8(igsd, (uint8_t)(b ? igbinary_type_bool_true : igbinary_type_bool_false)); } /* }}} */ /* {{{ igbinary_serialize_long */ /** Serializes zend_long. */ inline static int igbinary_serialize_long(struct igbinary_serialize_data *igsd, zend_long l) { const bool p = l >= 0; // k is the absolute value of l const zend_ulong k = p ? (zend_ulong)l : -(zend_ulong)l; if (k <= 0xff) { RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_8(igsd, p ? igbinary_type_long8p : igbinary_type_long8n, (uint8_t)k )); } else if (k <= 0xffff) { RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_16(igsd, p ? igbinary_type_long16p : igbinary_type_long16n, (uint16_t)k )); #if SIZEOF_ZEND_LONG == 8 } else if (k <= 0xffffffff) { RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_32(igsd, p ? igbinary_type_long32p : igbinary_type_long32n, (uint32_t)k )); } else { RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_64(igsd, p ? igbinary_type_long64p : igbinary_type_long64n, (uint64_t)k )); } #elif SIZEOF_ZEND_LONG == 4 } else { RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_32(igsd, p ? igbinary_type_long32p : igbinary_type_long32n, (uint32_t)k )); } #else #error "Strange sizeof(zend_long)." #endif return 0; } /* }}} */ /* {{{ igbinary_serialize_double */ /** Serializes double. */ inline static int igbinary_serialize_double(struct igbinary_serialize_data *igsd, double d) { union { double d; uint64_t u; } u; u.d = d; return igbinary_serialize8_and_64(igsd, igbinary_type_double, u.u); } /* }}} */ /* {{{ igbinary_serialize_string */ /** * Serializes string. * * When compact_strings is true, * this will serialize the string as igbinary_type_string* (A length followed by a character array) the first time, * and serialize subsequent references to the same string as igbinary_type_string_id*. * * When compact_strings is false, this will always serialize the string as a character array. * compact_strings speeds up serialization, but slows down serialization and uses more space to represent the serialization. * * Serializes each string once. * After first time uses pointers (igbinary_type_string_id*) instead of igbinary_type_string*. */ inline static int igbinary_serialize_string(struct igbinary_serialize_data *igsd, zend_string *s) { const size_t len = ZSTR_LEN(s); if (len == 0) { /* The empty string is always serialized as igbinary_serialize_string (1 byte instead of 2) */ return igbinary_serialize8(igsd, igbinary_type_string_empty); } if (!igsd->scalar && igsd->compact_strings) { struct hash_si_result result = hash_si_find_or_insert(&igsd->strings, s, igsd->string_count); if (result.code == hash_si_code_exists) { uint32_t value = result.value; if (value <= 0xff) { RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_8(igsd, (uint8_t)igbinary_type_string_id8, (uint8_t)value)); } else if (value <= 0xffff) { RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_16(igsd, (uint8_t)igbinary_type_string_id16, (uint16_t)value)); } else { RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_32(igsd, (uint8_t)igbinary_type_string_id32, value)); } return 0; } else if (EXPECTED(result.code == hash_si_code_inserted)) { /* Fall through to igbinary_serialize_chararray */ } else { return 1; /* Failed to allocate copy of string */ } } igsd->string_count++; /* A new string is being serialized - update count so that duplicate class names can be used. */ if (UNEXPECTED(igsd->string_count == 0)) { zend_error(E_WARNING, "igbinary_serialize: Saw too many strings"); return 1; } return igbinary_serialize_chararray(igsd, ZSTR_VAL(s), len); } /* }}} */ #if SIZEOF_SIZE_T > 4 static ZEND_COLD zend_never_inline int igbinary_serialize_extremely_long_chararray(struct igbinary_serialize_data *igsd, const char *s, size_t len) { RETURN_1_IF_NON_ZERO(igbinary_serialize_resize(igsd, len + 9)); uint8_t *append_buffer = &igsd->buffer[igsd->buffer_size]; append_buffer[0] = igbinary_type_string64; append_buffer[1] = (uint8_t)(len >> 56 & 0xff); append_buffer[2] = (uint8_t)(len >> 48 & 0xff); append_buffer[3] = (uint8_t)(len >> 40 & 0xff); append_buffer[4] = (uint8_t)(len >> 32 & 0xff); append_buffer[5] = (uint8_t)(len >> 24 & 0xff); append_buffer[6] = (uint8_t)(len >> 16 & 0xff); append_buffer[7] = (uint8_t)(len >> 8 & 0xff); append_buffer[8] = (uint8_t)(len & 0xff); memcpy(append_buffer + 9, s, len); igsd->buffer_size += 9 + len; return 0; } #endif /* {{{ igbinary_serialize_chararray */ /** Serializes string data as the type followed by the length followed by the raw character array. */ inline static int igbinary_serialize_chararray(struct igbinary_serialize_data *igsd, const char *s, size_t len) { uint8_t *append_buffer; int offset; if (len <= 0xff) { RETURN_1_IF_NON_ZERO(igbinary_serialize_resize(igsd, len + 2)); append_buffer = &igsd->buffer[igsd->buffer_size]; append_buffer[0] = igbinary_type_string8; append_buffer[1] = len; offset = 2; } else if (len <= 0xffff) { RETURN_1_IF_NON_ZERO(igbinary_serialize_resize(igsd, len + 3)); append_buffer = &igsd->buffer[igsd->buffer_size]; append_buffer[0] = igbinary_type_string16; append_buffer[1] = (uint8_t)(len >> 8 & 0xff); append_buffer[2] = (uint8_t)(len & 0xff); offset = 3; } else { #if SIZEOF_SIZE_T > 4 if (UNEXPECTED(len > 0xffffffff)) { return igbinary_serialize_extremely_long_chararray(igsd, s, len); } #endif RETURN_1_IF_NON_ZERO(igbinary_serialize_resize(igsd, len + 5)); append_buffer = &igsd->buffer[igsd->buffer_size]; append_buffer[0] = igbinary_type_string32; append_buffer[1] = (uint8_t)(len >> 24 & 0xff); append_buffer[2] = (uint8_t)(len >> 16 & 0xff); append_buffer[3] = (uint8_t)(len >> 8 & 0xff); append_buffer[4] = (uint8_t)(len & 0xff); offset = 5; } memcpy(append_buffer + offset, s, len); igsd->buffer_size += offset + len; return 0; } /* }}} */ /* {{{ igbinary_serialize_array */ /** * Serializes an array's or object's inner properties. * If properties or keys are unexpectedly added (e.g. by __sleep() or serialize() elsewhere), this will skip serializing them. * If properties or keys are unexpectedly removed, this will add igbinary_type_null as padding for the corresponding entries. */ inline static int igbinary_serialize_array(struct igbinary_serialize_data *igsd, zval *z, bool object, bool incomplete_class, bool serialize_props) { /* If object=true: z is IS_OBJECT */ /* If object=false: z is either IS_ARRAY, or IS_REFERENCE pointing to an IS_ARRAY. */ HashTable *h; /* At the time of writing, struct _zend_array had uint32_t nNumOfElements */ uint32_t n; zval *d; zval *z_original; zend_string *key; zend_long key_index; z_original = z; ZVAL_DEREF(z); ZEND_ASSERT((!object || !serialize_props) && (object || !incomplete_class)); /* hash */ if (object) { h = zend_get_properties_for(z, ZEND_PROP_PURPOSE_SERIALIZE); n = h ? zend_hash_num_elements(h) : 0; /* incomplete class magic member */ if (incomplete_class && n > 0) { --n; } } else { h = Z_ARRVAL_P(z); n = zend_hash_num_elements(h); /* if it is an array or a reference to an array, then add a reference unique to that **reference** to that array */ if (serialize_props) { int ref_ser = igbinary_serialize_array_ref(igsd, z_original, false); if (ref_ser != 2) { /* 1 means out of memory or error, 0 means this already exists, 2 means this is new. */ return ref_ser; } } } if (n <= 0xff) { RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_8(igsd, igbinary_type_array8, (uint8_t)n)); if (n == 0) { if (object) { zend_release_properties(h); } return 0; } } else if (n <= 0xffff) { RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_16(igsd, igbinary_type_array16, (uint16_t)n)); } else { RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_32(igsd, igbinary_type_array32, n)); } /* serialize properties. */ ZEND_HASH_FOREACH_KEY_VAL(h, key_index, key, d) { /* skip magic member in incomplete classes */ /* Match it exactly and permit null bytes in the middle so that the counts will match. */ if (incomplete_class && zend_string_equals_literal(key, MAGIC_MEMBER)) { continue; } /* https://wiki.php.net/phpng-int - This is a declared property of an object, or an element of $GLOBALS */ if (Z_TYPE_P(d) == IS_INDIRECT) { d = Z_INDIRECT_P(d); } if (Z_TYPE_P(d) == IS_UNDEF) { /* This is an undefined declared typed property of an object. */ /* This can't be a value or a reference in an array - except maybe for $GLOBALS, which has other issues. */ ZEND_ASSERT(!serialize_props); RETURN_1_IF_NON_ZERO(igbinary_serialize_null(igsd)); continue; } if (key == NULL) { /* Key is numeric */ RETURN_1_IF_NON_ZERO(igbinary_serialize_long(igsd, key_index)); } else { /* Key is string */ RETURN_1_IF_NON_ZERO(igbinary_serialize_string(igsd, key)); } /* we should still add element even if it's not OK, * since we already wrote the length of the array before */ RETURN_1_IF_NON_ZERO(igbinary_serialize_zval(igsd, d)); } ZEND_HASH_FOREACH_END(); if (object) { zend_release_properties(h); } return 0; } /* }}} */ /* {{{ igbinary_serialize_array_ref */ /** Serializes array reference (or reference in an object). Returns 0 on success. */ /* TODO: Use different result codes for missing keys and errors */ zend_always_inline static int igbinary_serialize_array_ref(struct igbinary_serialize_data *igsd, zval * const z, bool object) { size_t t; zend_uintptr_t key; /* The address of the pointer to the zend_refcounted struct or other struct */ static int INVALID_KEY; /* Not used, but we take the pointer of this knowing other zvals won't share it*/ /* Similar to php_var_serialize_intern's first part, as well as php_add_var_hash, for printing R: (reference) or r:(object) */ /* However, it differs from the built in serialize() in that references to objects are preserved when serializing and unserializing? (TODO check, test for backwards compatibility) */ ZEND_ASSERT(Z_ISREF_P(z) || (object && Z_TYPE_P(z) == IS_OBJECT) || Z_TYPE_P(z) == IS_ARRAY); // zend_bool is_ref = Z_ISREF_P(z); /* Do I have to dereference object references so that reference ids will be the same as in php5? */ /* If I do, then more tests fail. */ /* is_ref || IS_OBJECT implies it has a unique refcounted struct */ // NOTE: The original code would always use the same memory address - Z_COUNTED_P is the start of an object/array/reference // if (object && Z_TYPE_P(z) == IS_OBJECT) { // key = (zend_uintptr_t)Z_OBJ_P(z); /* expand object handle(uint32_t), cast to 32-bit/64-bit pointer */ // } else if (is_ref) { // /* NOTE: PHP switched from `zval*` to `zval` for the values stored in HashTables. // * If an array has two references to the same ZVAL, then those references will have different zvals. // * We use Z_COUNTED_P(ref), which will be the same if (and only if) the references are the same. */ // /* is_ref implies there is a unique reference counting pointer for the reference */ // key = (zend_uintptr_t)Z_COUNTED_P(z); // } else if (EXPECTED(Z_TYPE_P(z) == IS_ARRAY)) { // key = (zend_uintptr_t)Z_ARR_P(z); // } else { // /* Nothing else is going to reference this when this is serialized, this isn't ref counted or an object, shouldn't be reached. */ // /* Increment the reference id for the deserializer, give up. */ // ++igsd->references_id; // php_error_docref(NULL, E_NOTICE, "igbinary_serialize_array_ref expected either object or reference (param object=%s), got neither (zend_type=%d)", object ? "true" : "false", (int)Z_TYPE_P(z)); // return 1; // } /* FIXME hack? If the top-level element was an array, we assume that it can't be a reference when we serialize it, */ /* because that's the way it was serialized in php5. */ /* Does this work with different forms of recursive arrays? */ if (igsd->references_id == 0 && !object) { key = (zend_uintptr_t)&INVALID_KEY; } else { key = (zend_uintptr_t)z->value.ptr; } t = hash_si_ptr_find_or_insert(&igsd->references, key, igsd->references_id); if (t == SIZE_MAX) { /* This is a brand new object/array/reference entry with a key equal to igsd->references_id */ igsd->references_id++; if (UNEXPECTED(igsd->references_id == 0)) { zend_error(E_WARNING, "igbinary_serialize: Saw too many references"); return 1; } /* We only need to call this if the array/object is new, in case __serialize or other methods return temporary arrays or modify arrays that were serialized earlier */ igsd_addref_and_defer_dtor(&igsd->deferred_dtor_tracker, z); return 2; } /* TODO: Properly handle running out of memory in this helper function. */ /* It returns 1 both for running out of memory and inserting a new entry. */ enum igbinary_type type; if (t <= 0xff) { type = object ? igbinary_type_objref8 : igbinary_type_ref8; RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_8(igsd, (uint8_t)type, (uint8_t)t)); } else if (t <= 0xffff) { type = object ? igbinary_type_objref16 : igbinary_type_ref16; RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_16(igsd, (uint8_t)type, (uint16_t)t)) } else { type = object ? igbinary_type_objref32 : igbinary_type_ref32; RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_32(igsd, (uint8_t)type, (uint32_t)t)) } return 0; } /* }}} */ /* {{{ igbinary_serialize_array_sleep_single_prop_value */ /** Serializes one value of an object's properties array, for use with the __sleep function. */ inline static int igbinary_serialize_array_sleep_single_prop_value(struct igbinary_serialize_data *igsd, zval *z, zval *v, zend_class_entry *ce, zend_string *prop_name) { /* Precondition: All args are non-null */ if (Z_TYPE_P(v) == IS_INDIRECT) { v = Z_INDIRECT_P(v); if (UNEXPECTED(Z_TYPE_P(v) == IS_UNDEF)) { #if PHP_VERSION_ID >= 70400 if (UNEXPECTED(zend_get_typed_property_info_for_slot(Z_OBJ_P(z), v) != NULL)) { zend_throw_error(NULL, "Typed property %s::$%s must not be accessed before initialization (in __sleep)", ZSTR_VAL(ce->name), ZSTR_VAL(prop_name)); return 1; } #endif goto serialize_untyped_uninitialized_prop; } } else { if (UNEXPECTED(Z_TYPE_P(v) == IS_UNDEF)) { serialize_untyped_uninitialized_prop: php_error_docref(NULL, E_NOTICE, "\"%s\" returned as member variable from __sleep() but does not exist", ZSTR_VAL(prop_name)); return igbinary_serialize_null(igsd); } } return igbinary_serialize_zval(igsd, v); } /* }}} */ /* {{{ igbinary_serialize_array_sleep_inner */ /** Serializes object's properties array with __sleep -function. */ inline static int igbinary_serialize_array_sleep_inner(struct igbinary_serialize_data *igsd, zval *z, HashTable *h, HashTable *object_properties, zend_class_entry *ce) { zval *d; zval *v; ZEND_HASH_FOREACH_VAL(h, d) { if (UNEXPECTED(d == NULL || Z_TYPE_P(d) != IS_STRING)) { php_error_docref(NULL, E_NOTICE, "__sleep should return an array only " "containing the names of instance-variables to " "serialize"); /* we should still add element even if it's not OK, * since we already wrote the length of the array before * serialize null as key-value pair */ /* TODO: Allow creating a tmp string, like php's serialize() */ RETURN_1_IF_NON_ZERO(igbinary_serialize_null(igsd)); continue; } zend_string *prop_name = Z_STR_P(d); if ((v = zend_hash_find(object_properties, prop_name)) != NULL) { RETURN_1_IF_NON_ZERO(igbinary_serialize_string(igsd, prop_name)); RETURN_1_IF_NON_ZERO(igbinary_serialize_array_sleep_single_prop_value(igsd, z, v, ce, prop_name)); } else { zend_string *mangled_prop_name; v = NULL; int res; /* try private */ mangled_prop_name = zend_mangle_property_name(ZSTR_VAL(ce->name), ZSTR_LEN(ce->name), ZSTR_VAL(prop_name), ZSTR_LEN(prop_name), 0); v = zend_hash_find(object_properties, mangled_prop_name); /* try protected */ if (v == NULL) { zend_string_efree(mangled_prop_name); mangled_prop_name = zend_mangle_property_name("*", 1, ZSTR_VAL(prop_name), ZSTR_LEN(prop_name), 0); v = zend_hash_find(object_properties, mangled_prop_name); /* Neither protected nor private property exists */ if (v == NULL) { zend_string_efree(mangled_prop_name); php_error_docref(NULL, E_NOTICE, "\"%s\" returned as member variable from __sleep() but does not exist", ZSTR_VAL(prop_name)); RETURN_1_IF_NON_ZERO(igbinary_serialize_string(igsd, prop_name)); RETURN_1_IF_NON_ZERO(igbinary_serialize_null(igsd)); continue; } } res = igbinary_serialize_string(igsd, mangled_prop_name); /* igbinary_serialize_string will increase the reference count. */ zend_string_release_ex(mangled_prop_name, 0); RETURN_1_IF_NON_ZERO(res); RETURN_1_IF_NON_ZERO(igbinary_serialize_array_sleep_single_prop_value(igsd, z, v, ce, prop_name)); } } ZEND_HASH_FOREACH_END(); return 0; } /* }}} */ /* {{{ igbinary_serialize_array_sleep */ /** Serializes object's properties array with __sleep -function. */ inline static int igbinary_serialize_array_sleep(struct igbinary_serialize_data *igsd, zval *z, HashTable *h, zend_class_entry *ce) { HashTable *object_properties; uint32_t n = zend_hash_num_elements(h); /* Serialize array id. */ if (n <= 0xff) { RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_8(igsd, igbinary_type_array8, (uint8_t)n)) if (n == 0) { return 0; } } else if (n <= 0xffff) { RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_16(igsd, igbinary_type_array16, (uint16_t)n)) } else { RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_32(igsd, igbinary_type_array32, n)) } object_properties = zend_get_properties_for(z, ZEND_PROP_PURPOSE_SERIALIZE); int r = igbinary_serialize_array_sleep_inner(igsd, z, h, object_properties, ce); zend_release_properties(object_properties); return r; } /* }}} */ /* {{{ igbinary_serialize_object_name */ /** * Serialize a PHP object's class name. * Note that this deliberately ignores the compact_strings setting. */ inline static int igbinary_serialize_object_name(struct igbinary_serialize_data *igsd, zend_string *class_name) { struct hash_si_result result = hash_si_find_or_insert(&igsd->strings, class_name, igsd->string_count); if (result.code == hash_si_code_inserted) { const size_t name_len = ZSTR_LEN(class_name); igsd->string_count += 1; if (UNEXPECTED(igsd->string_count == 0)) { zend_error(E_WARNING, "igbinary_serialize: Saw too many strings"); return 1; } if (name_len <= 0xff) { RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_8(igsd, igbinary_type_object8, (uint8_t)name_len)) } else if (EXPECTED(name_len <= 0xffff)) { RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_16(igsd, igbinary_type_object16, (uint16_t)name_len)) } else { #if SIZEOF_SIZE_T > 4 if (UNEXPECTED(name_len > 0xffffffff)) { zend_error(E_WARNING, "igbinary_serialize_object_name: class name does not fit in 32 bits"); return 1; } #endif RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_32(igsd, igbinary_type_object32, (uint32_t)name_len)) } RETURN_1_IF_NON_ZERO(igbinary_serialize_resize(igsd, name_len)); memcpy(igsd->buffer + igsd->buffer_size, ZSTR_VAL(class_name), name_len); igsd->buffer_size += name_len; } else if (EXPECTED(result.code == hash_si_code_exists)) { /* already serialized string */ uint32_t value = result.value; if (value <= 0xff) { RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_8(igsd, (uint8_t)igbinary_type_object_id8, (uint8_t)value)) } else if (value <= 0xffff) { RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_16(igsd, (uint8_t)igbinary_type_object_id16, (uint16_t)value)) } else { RETURN_1_IF_NON_ZERO(igbinary_serialize8_and_32(igsd, (uint8_t)igbinary_type_object_id32, (uint32_t)value)) } } else { return 1; /* Failed to allocate copy of string */ } return 0; } /* }}} */ /* igbinary_serialize_object_old_serializer_class {{{ */ static ZEND_COLD int igbinary_serialize_object_old_serializer_class(struct igbinary_serialize_data* igsd, zval *z, zend_class_entry *ce) { unsigned char *serialized_data = NULL; size_t serialized_len; int r = 0; if (ce->serialize(z, &serialized_data, &serialized_len, (zend_serialize_data *)NULL) == SUCCESS && !EG(exception)) { if (UNEXPECTED(igbinary_serialize_object_name(igsd, ce->name) != 0)) { goto failure; } if (serialized_len <= 0xff) { if (UNEXPECTED(igbinary_serialize8_and_8(igsd, igbinary_type_object_ser8, (uint8_t)serialized_len) != 0)) { goto failure; } } else if (serialized_len <= 0xffff) { if (UNEXPECTED(igbinary_serialize8_and_16(igsd, (uint8_t)igbinary_type_object_ser16, (uint16_t)serialized_len) != 0)) { goto failure; } } else { #if SIZEOF_ZEND_LONG > 4 if (UNEXPECTED(serialized_len > 0xffffffff)) { zend_error(E_WARNING, "igbinary_serialize_object_old_serializer_class: serialize() data 4GB or larger is not supported"); goto failure; } #endif if (UNEXPECTED(igbinary_serialize8_and_32(igsd, (uint8_t)igbinary_type_object_ser32, (uint32_t)serialized_len) != 0)) { goto failure; } } if (UNEXPECTED(igbinary_serialize_resize(igsd, serialized_len))) { goto failure; } memcpy(igsd->buffer + igsd->buffer_size, serialized_data, serialized_len); igsd->buffer_size += serialized_len; } else if (EG(exception)) { /* exception, return failure */ failure: r = 1; } else { /* Serialization callback failed, assume null output */ r = igbinary_serialize_null(igsd); } if (serialized_data) { efree(serialized_data); } return r; } /* }}} */ /* igbinary_var_serialize_call_magic_serialize {{{ */ // Source: ext/standard/var.c from php-src #if PHP_VERSION_ID >= 70400 inline static int igbinary_var_serialize_call_magic_serialize(zval *retval, zval *obj) { #if PHP_VERSION_ID >= 80000 BG(serialize_lock)++; zend_call_known_instance_method_with_0_params( Z_OBJCE_P(obj)->__serialize, Z_OBJ_P(obj), retval); BG(serialize_lock)--; if (EG(exception)) { zval_ptr_dtor(retval); return 1; } #else zval fname; int res; ZVAL_STRINGL(&fname, "__serialize", sizeof("__serialize") - 1); // XXX does this work with the standard serializer? BG(serialize_lock)++; res = call_user_function(CG(function_table), obj, &fname, retval, 0, 0); BG(serialize_lock)--; zval_ptr_dtor_str(&fname); if (res == FAILURE || Z_ISUNDEF_P(retval)) { zval_ptr_dtor(retval); return 1; } #endif if (Z_TYPE_P(retval) != IS_ARRAY) { zval_ptr_dtor(retval); zend_type_error("%s::__serialize() must return an array", ZSTR_VAL(Z_OBJCE_P(obj)->name)); return 1; } return 0; } #endif /* }}} */ /* igbinary_serialize_object_new_serializer {{{ */ #if PHP_VERSION_ID >= 70400 inline static int igbinary_serialize_object_new_serializer(struct igbinary_serialize_data* igsd, zval *z, zend_class_entry *ce) { zval retval; int r = 0; // Call retval = __serialize(z), which returns an array or fails. if (igbinary_var_serialize_call_magic_serialize(&retval, z)) { // retval is already freed if (!EG(exception)) { // When does this happen? igbinary_serialize_null(igsd); return 0; } return 1; } // fprintf(stderr, "Serialize returned a value of type %d\n", Z_TYPE(retval)); if (UNEXPECTED(igbinary_serialize_object_name(igsd, ce->name) != 0)) { zval_ptr_dtor(&retval); return 1; } // This serializes an object name followed by an array, in the same format used when no serializers exist. // object is false because this is the array returned by __serialize() for the object. // TODO: Add tests that this works properly when the same array is reused. r = igbinary_serialize_array(igsd, &retval, false, false, false); zval_ptr_dtor(&retval); if (UNEXPECTED(r)) { return 1; } return EG(exception) != NULL; } #endif /* }}} */ /* {{{ igbinary_serialize_object_enum_case */ #if PHP_VERSION_ID >= 80100 inline static int igbinary_serialize_object_enum_case(struct igbinary_serialize_data *igsd, zend_object *obj, zend_class_entry *ce) { if (UNEXPECTED(igbinary_serialize_object_name(igsd, ce->name) != 0)) { return 1; } if (UNEXPECTED(igbinary_serialize8(igsd, (uint8_t)igbinary_type_enum_case))) { return 1; } zval *case_name = zend_enum_fetch_case_name(obj); ZEND_ASSERT(Z_TYPE_P(case_name) == IS_STRING); return igbinary_serialize_string(igsd, Z_STR_P(case_name)); } #endif /* }}} */ /* {{{ igbinary_serialize_object */ /** Serialize object. * @see ext/standard/var.c * */ inline static int igbinary_serialize_object(struct igbinary_serialize_data *igsd, zval *z) { PHP_CLASS_ATTRIBUTES; zend_class_entry *ce; int r = 0; int ref_ser = igbinary_serialize_array_ref(igsd, z, true); if (ref_ser != 2) { return ref_ser; } ce = Z_OBJCE_P(z); if (IGBINARY_IS_NOT_SERIALIZABLE(ce)) { zend_throw_exception_ex(NULL, 0, "Serialization of '%s' is not allowed", ZSTR_VAL(ce->name)); return 1; } #if PHP_VERSION_ID >= 80100 if (ce->ce_flags & ZEND_ACC_ENUM) { return igbinary_serialize_object_enum_case(igsd, Z_OBJ_P(z), ce); } #endif #if PHP_VERSION_ID >= 70400 #if PHP_VERSION_ID >= 80000 if (ce->__serialize) #else if (zend_hash_str_exists(&ce->function_table, "__serialize", sizeof("__serialize")-1)) #endif { // fprintf(stderr, "Going to serialize %s\n", ZSTR_VAL(ce->name)); return igbinary_serialize_object_new_serializer(igsd, z, ce); } #endif if (ce->serialize != NULL) { return igbinary_serialize_object_old_serializer_class(igsd, z, ce); } /* serialize class name */ PHP_SET_CLASS_ATTRIBUTES(z); if (igbinary_serialize_object_name(igsd, class_name) != 0) { PHP_CLEANUP_CLASS_ATTRIBUTES(); return 1; } PHP_CLEANUP_CLASS_ATTRIBUTES(); if (ce && ce != PHP_IC_ENTRY && zend_hash_str_exists(&ce->function_table, "__sleep", sizeof("__sleep") - 1)) { zval h; zval f; /* function name string */ /* TODO use ZSTR_KNOWN(ZEND_STR_SLEEP) instead when available*/ /* zval *zv = zend_hash_find_known_hash(&ce->function_table, ZSTR_KNOWN(ZEND_STR_SLEEP)); */ ZVAL_STRINGL(&f, "__sleep", sizeof("__sleep") - 1); /* calling z->__sleep */ r = call_user_function(CG(function_table), z, &f, &h, 0, 0); zval_ptr_dtor_str(&f); if (r == SUCCESS && !EG(exception)) { HashTable *ht; r = 0; if (Z_TYPE(h) == IS_UNDEF) { /* FIXME: is this ok? */ /* Valid, but skip */ } else if ((ht = HASH_OF(&h)) != NULL) { /* NOTE: PHP permits returning an object in __sleep */ r = igbinary_serialize_array_sleep(igsd, z, ht, ce); } else { php_error_docref(NULL, E_NOTICE, "__sleep should return an array only " "containing the names of instance-variables to " "serialize"); /* empty array */ r = igbinary_serialize8_and_8(igsd, igbinary_type_array8, 0); } } else { r = 1; } /* cleanup */ zval_ptr_dtor(&h); return r; } return igbinary_serialize_array(igsd, z, true, incomplete_class, false); } /* }}} */ /* {{{ igbinary_warn_serialize_resource */ static ZEND_COLD int igbinary_warn_serialize_resource(zval *z) { const char *resource_type; resource_type = zend_rsrc_list_get_rsrc_type(Z_RES_P(z)); if (!resource_type) { resource_type = "Unknown"; } php_error_docref(NULL, E_DEPRECATED, "Cannot serialize resource(%s) and resources may be converted to objects that cannot be serialized in future php releases. Serializing the value as null instead", resource_type); return EG(exception) != NULL; } /* }}} */ /* {{{ igbinary_serialize_zval */ /** Serialize zval. */ static int igbinary_serialize_zval(struct igbinary_serialize_data *igsd, zval *z) { if (Z_ISREF_P(z)) { if (Z_REFCOUNT_P(z) >= 2) { RETURN_1_IF_NON_ZERO(igbinary_serialize8(igsd, (uint8_t)igbinary_type_ref)) switch (Z_TYPE_P(Z_REFVAL_P(z))) { case IS_ARRAY: return igbinary_serialize_array(igsd, z, false, false, true); case IS_OBJECT: break; /* Fall through */ default: { /* Serialize a reference if zval already added */ int ref_ser = igbinary_serialize_array_ref(igsd, z, false); if (ref_ser != 2) { return ref_ser; } /* Fall through */ } } } z = Z_REFVAL_P(z); } switch (Z_TYPE_P(z)) { case IS_RESOURCE: if (igbinary_warn_serialize_resource(z)) { return 1; } return igbinary_serialize_null(igsd); case IS_OBJECT: return igbinary_serialize_object(igsd, z); case IS_ARRAY: /* if is_ref, then php5 would have called igbinary_serialize_array_ref */ return igbinary_serialize_array(igsd, z, false, false, true); case IS_STRING: return igbinary_serialize_string(igsd, Z_STR_P(z)); case IS_LONG: return igbinary_serialize_long(igsd, Z_LVAL_P(z)); case IS_UNDEF: // https://github.com/igbinary/igbinary/issues/134 // TODO: In a new major version, could have a separate type for IS_UNDEF, which would unset the property in an object context? // fallthrough case IS_NULL: return igbinary_serialize_null(igsd); case IS_TRUE: return igbinary_serialize_bool(igsd, 1); case IS_FALSE: return igbinary_serialize_bool(igsd, 0); case IS_DOUBLE: return igbinary_serialize_double(igsd, Z_DVAL_P(z)); default: zend_error(E_ERROR, "igbinary_serialize_zval: zval has unknown type %d", (int)Z_TYPE_P(z)); return 1; } return 0; } /* }}} */ /* {{{ igbinary_unserialize_data_init */ /** Inits igbinary_unserialize_data. */ inline static int igbinary_unserialize_data_init(struct igbinary_unserialize_data *igsd) { struct igbinary_value_ref *references = emalloc(sizeof(igsd->references[0]) * 4); zend_string **strings; if (UNEXPECTED(references == NULL)) { return 1; } strings = (zend_string **)emalloc(sizeof(zend_string *) * 4); if (UNEXPECTED(strings == NULL)) { /* We failed to allocate memory for strings. Fail and free everything we allocated */ efree(references); return 1; } igsd->buffer = NULL; igsd->buffer_end = NULL; igsd->buffer_ptr = NULL; igsd->strings = NULL; igsd->strings_count = 0; igsd->strings_capacity = 4; igsd->references = references; igsd->references_count = 0; igsd->references_capacity = 4; igsd->strings = strings; /** Don't bother allocating zvals which __wakeup or __unserialize, probably not common */ igsd->deferred = NULL; igsd->deferred_count = 0; igsd->deferred_capacity = 0; igsd->deferred_finished = 0; igsd->deferred_dtor_tracker.zvals = NULL; igsd->deferred_dtor_tracker.count = 0; igsd->deferred_dtor_tracker.capacity = 0; #if PHP_VERSION_ID >= 70400 igsd->ref_props = NULL; #endif return 0; } /* }}} */ /* {{{ igbinary_unserialize_data_deinit */ /** Deinits igbinary_unserialize_data. */ inline static void igbinary_unserialize_data_deinit(struct igbinary_unserialize_data *igsd) { if (igsd->strings) { size_t i; size_t strings_count = igsd->strings_count; zend_string **strings = igsd->strings; for (i = 0; i < strings_count; i++) { zend_string *s = strings[i]; #if ZEND_DEBUG ZEND_ASSERT(GC_REFCOUNT(s) >= 1); #endif zend_string_release_ex(s, 0); /* Should only create interned or non-persistent strings when unserializing */ } efree(strings); } if (igsd->references) { efree(igsd->references); } if (igsd->deferred) { struct deferred_call *calls = igsd->deferred; #if PHP_VERSION_ID >= 70400 uint32_t i; uint32_t n = igsd->deferred_count; for (i = 0; i < n; i++) { struct deferred_call *call = &calls[i]; if (call->is_unserialize) { if (!igsd->deferred_finished) { struct deferred_unserialize_call *unserialize_call = &call->data.unserialize; GC_ADD_FLAGS(unserialize_call->object, IS_OBJ_DESTRUCTOR_CALLED); zval_ptr_dtor(&unserialize_call->param); } } } #endif efree(calls); } free_deferred_dtors(&igsd->deferred_dtor_tracker); #if PHP_VERSION_ID >= 70400 if (igsd->ref_props) { zend_hash_destroy(igsd->ref_props); FREE_HASHTABLE(igsd->ref_props); } #endif return; } /* }}} */ /* {{{ igbinary_unserialize_header_emit_warning */ /** * Warns about invalid byte headers * Precondition: igsd->buffer_size >= 4 */ static ZEND_COLD void igbinary_unserialize_header_emit_warning(struct igbinary_unserialize_data *igsd, int version) { int i; char buf[9], *it; for (i = 0; i < 4; i++) { if (!isprint((int)igsd->buffer[i])) { if (version != 0 && (((unsigned int)version) & 0xff000000) == (unsigned int)version) { // Check if high order byte was set instead of low order byte zend_error(E_WARNING, "igbinary_unserialize_header: unsupported version: %u, should be %u or %u (wrong endianness?)", (unsigned int)version, 0x00000001, (unsigned int)IGBINARY_FORMAT_VERSION); return; } // Binary data, or a version number from a future release. zend_error(E_WARNING, "igbinary_unserialize_header: unsupported version: %u, should be %u or %u", (unsigned int)version, 0x00000001, (unsigned int)IGBINARY_FORMAT_VERSION); return; } } /* To avoid confusion, if the first 4 bytes are all printable, print those instead of the integer representation to make debugging easier. */ /* E.g. strings such as "a:2:" are emitted when an Array is serialized with serialize() instead of igbinary_serialize(), */ /* and subsequently passed to igbinary_unserialize instead of unserialize(). */ for (it = buf, i = 0; i < 4; i++) { char c = igsd->buffer[i]; if (c == '"' || c == '\\') { *it++ = '\\'; } *it++ = c; } *it = '\0'; zend_error(E_WARNING, "igbinary_unserialize_header: unsupported version: \"%s\"..., should begin with a binary version header of \"\\x00\\x00\\x00\\x01\" or \"\\x00\\x00\\x00\\x%02x\"", buf, (int)IGBINARY_FORMAT_VERSION); } /* }}} */ /* {{{ igbinary_unserialize_header */ /** Unserialize header. Check for version. */ inline static int igbinary_unserialize_header(struct igbinary_unserialize_data *igsd) { uint32_t version; if (IGB_NEEDS_MORE_DATA(igsd, 5)) { zend_error(E_WARNING, "igbinary_unserialize_header: expected at least 5 bytes of data, got %u byte(s)", IGB_REMAINING_BYTES(igsd)); return 1; } version = igbinary_unserialize32(igsd); /* Support older version 1 and the current format 2 */ if (EXPECTED(version == IGBINARY_FORMAT_VERSION || version == 0x00000001)) { return 0; } else { igbinary_unserialize_header_emit_warning(igsd, version); return 1; } } /* }}} */ /* {{{ igbinary_unserialize8 */ /** Unserialize 8bit value. */ inline static uint8_t igbinary_unserialize8(struct igbinary_unserialize_data *igsd) { return *(igsd->buffer_ptr++); } /* }}} */ /* {{{ igbinary_unserialize16 */ /** Unserialize 16bit value. */ inline static uint16_t igbinary_unserialize16(struct igbinary_unserialize_data *igsd) { uint16_t ret = ((uint16_t)(igsd->buffer_ptr[0]) << 8) | ((uint16_t)(igsd->buffer_ptr[1])); igsd->buffer_ptr += 2; return ret; } /* }}} */ /* {{{ igbinary_unserialize32 */ /** Unserialize 32bit value. */ inline static uint32_t igbinary_unserialize32(struct igbinary_unserialize_data *igsd) { uint32_t ret = ((uint32_t)(igsd->buffer_ptr[0]) << 24) | ((uint32_t)(igsd->buffer_ptr[1]) << 16) | ((uint32_t)(igsd->buffer_ptr[2]) << 8) | ((uint32_t)(igsd->buffer_ptr[3])); igsd->buffer_ptr += 4; return ret; } /* }}} */ /* {{{ igbinary_unserialize64 */ /** Unserialize 64bit value. */ inline static uint64_t igbinary_unserialize64(struct igbinary_unserialize_data *igsd) { uint64_t ret = ((uint64_t)((igsd->buffer_ptr[0])) << 56) | ((uint64_t)((igsd->buffer_ptr[1])) << 48) | ((uint64_t)((igsd->buffer_ptr[2])) << 40) | ((uint64_t)((igsd->buffer_ptr[3])) << 32) | ((uint64_t)((igsd->buffer_ptr[4])) << 24) | ((uint64_t)((igsd->buffer_ptr[5])) << 16) | ((uint64_t)((igsd->buffer_ptr[6])) << 8) | ((uint64_t)((igsd->buffer_ptr[7])) << 0); igsd->buffer_ptr += 8; return ret; } /* }}} */ /* {{{ igbinary_unserialize_long */ /** Unserializes zend_long */ inline static int igbinary_unserialize_long(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zend_long *ret) { uint32_t tmp32; #if SIZEOF_ZEND_LONG == 8 uint64_t tmp64; #endif if (t == igbinary_type_long8p || t == igbinary_type_long8n) { if (IGB_NEEDS_MORE_DATA(igsd, 1)) { zend_error(E_WARNING, "igbinary_unserialize_long: end-of-data"); return 1; } *ret = (zend_long)(t == igbinary_type_long8n ? -1 : 1) * igbinary_unserialize8(igsd); } else if (t == igbinary_type_long16p || t == igbinary_type_long16n) { if (IGB_NEEDS_MORE_DATA(igsd, 2)) { zend_error(E_WARNING, "igbinary_unserialize_long: end-of-data"); return 1; } *ret = (zend_long)(t == igbinary_type_long16n ? -1 : 1) * igbinary_unserialize16(igsd); } else if (t == igbinary_type_long32p || t == igbinary_type_long32n) { if (IGB_NEEDS_MORE_DATA(igsd, 4)) { zend_error(E_WARNING, "igbinary_unserialize_long: end-of-data"); return 1; } /* check for boundaries (perform only one comparison in common case) */ tmp32 = igbinary_unserialize32(igsd); #if SIZEOF_ZEND_LONG == 4 if (UNEXPECTED(tmp32 >= 0x80000000 && (tmp32 > 0x80000000 || t == igbinary_type_long32p))) { zend_error(E_WARNING, "igbinary_unserialize_long: 64bit long on 32bit platform?"); tmp32 = 0; /* t == igbinary_type_long32p ? LONG_MAX : LONG_MIN; */ } #endif *ret = (zend_long)(t == igbinary_type_long32n ? -1 : 1) * tmp32; } else { ZEND_ASSERT(t == igbinary_type_long64p || t == igbinary_type_long64n); #if SIZEOF_ZEND_LONG == 8 if (IGB_NEEDS_MORE_DATA(igsd, 8)) { zend_error(E_WARNING, "igbinary_unserialize_long: end-of-data"); return 1; } /* check for boundaries (perform only one comparison in common case) */ tmp64 = igbinary_unserialize64(igsd); if (UNEXPECTED(tmp64 >= 0x8000000000000000 && (tmp64 > 0x8000000000000000 || t == igbinary_type_long64p))) { zend_error(E_WARNING, "igbinary_unserialize_long: too big 64bit long."); tmp64 = 0; /* t == igbinary_type_long64p ? LONG_MAX : LONG_MIN */ } *ret = (zend_long)(t == igbinary_type_long64n ? -1 : 1) * tmp64; #elif SIZEOF_ZEND_LONG == 4 /* can't put 64bit long into 32bit one, placeholder zero */ *ret = 0; igbinary_unserialize64(igsd); zend_error(E_WARNING, "igbinary_unserialize_long: 64bit long on 32bit platform"); #else #error "Strange sizeof(zend_long)." #endif } return 0; } /* }}} */ /* {{{ igbinary_unserialize_double */ /** Unserializes double. */ inline static int igbinary_unserialize_double(struct igbinary_unserialize_data *igsd, double *ret) { union { double d; uint64_t u; } u; if (IGB_NEEDS_MORE_DATA(igsd, 8)) { zend_error(E_WARNING, "igbinary_unserialize_double: end-of-data"); return 1; } u.u = igbinary_unserialize64(igsd); *ret = u.d; return 0; } /* }}} */ /* {{{ igbinary_unserialize_string */ /** Unserializes string. Unserializes both actual string or by string id. Returns NULL on error. */ inline static zend_string *igbinary_unserialize_string(struct igbinary_unserialize_data *igsd, enum igbinary_type t) { size_t i; zend_string *zstr; if (t == igbinary_type_string_id8 || t == igbinary_type_object_id8) { if (IGB_NEEDS_MORE_DATA(igsd, 1)) { zend_error(E_WARNING, "igbinary_unserialize_string: end-of-data"); return NULL; } i = igbinary_unserialize8(igsd); } else if (t == igbinary_type_string_id16 || t == igbinary_type_object_id16) { if (IGB_NEEDS_MORE_DATA(igsd, 2)) { zend_error(E_WARNING, "igbinary_unserialize_string: end-of-data"); return NULL; } i = igbinary_unserialize16(igsd); } else if (t == igbinary_type_string_id32 || t == igbinary_type_object_id32) { if (IGB_NEEDS_MORE_DATA(igsd, 4)) { zend_error(E_WARNING, "igbinary_unserialize_string: end-of-data"); return NULL; } i = igbinary_unserialize32(igsd); } else { zend_error(E_WARNING, "igbinary_unserialize_string: unknown type '%02x', position %zu", t, (size_t)IGB_BUFFER_OFFSET(igsd)); return NULL; } if (i >= igsd->strings_count) { zend_error(E_WARNING, "igbinary_unserialize_string: string index is out-of-bounds"); return NULL; } zstr = igsd->strings[i]; // Add one more ref (currently not using any interned strings) - Callers of this will decrease refs as needed #if PHP_VERSION_ID >= 80100 zend_string_addref(zstr); #else ZEND_ASSERT(!ZSTR_IS_INTERNED(zstr)); GC_ADDREF(zstr); #endif return zstr; } /* }}} */ /* igbinary_unserialize_extremely_long_chararray {{{ */ static ZEND_COLD zend_never_inline zend_string* igbinary_unserialize_extremely_long_chararray(struct igbinary_unserialize_data *igsd) { #if SIZEOF_SIZE_T <= 4 (void)igsd; zend_error(E_WARNING, "igbinary_unserialize_chararray: cannot unserialize 64-bit data on 32-bit platform"); return NULL; #else if (IGB_NEEDS_MORE_DATA(igsd, 8)) { zend_error(E_WARNING, "igbinary_unserialize_chararray: end-of-data"); return NULL; } size_t l = igbinary_unserialize64(igsd); if (IGB_NEEDS_MORE_DATA(igsd, l)) { zend_error(E_WARNING, "igbinary_unserialize_chararray: end-of-data"); return NULL; } if (igsd->strings_count + 1 > igsd->strings_capacity) { zend_string **new_strings; igsd->strings_capacity *= 2; new_strings = (zend_string **)erealloc(igsd->strings, sizeof(zend_string *) * igsd->strings_capacity); if (new_strings == NULL) { // The cleanup function will take care of destroying the allocated zend_strings. return NULL; } igsd->strings = new_strings; } zend_string *zstr = zend_string_init((const char*)igsd->buffer_ptr, l, 0); igsd->buffer_ptr += l; GC_ADDREF(zstr); /* definitely not interned. Add a reference in case the first reference gets deleted before reusing the temporary string */ igsd->strings[igsd->strings_count] = zstr; igsd->strings_count += 1; return zstr; #endif } /* }}} */ /* {{{ igbinary_unserialize_chararray */ /** Unserializes chararray of string. Returns NULL on error. */ inline static zend_string *igbinary_unserialize_chararray(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zend_bool check_interned) { size_t l; zend_string *zstr; if (t == igbinary_type_string8 || t == igbinary_type_object8) { if (IGB_NEEDS_MORE_DATA(igsd, 1)) { zend_error(E_WARNING, "igbinary_unserialize_chararray: end-of-data"); return NULL; } l = igbinary_unserialize8(igsd); /* Requires converting these into interned strings. Maybe add one-char in v3 of igbinary format? if (l == 1) { zstr = ZSTR_CHAR((zend_uchar)igsd->buffer[igsd->buffer_offset]); igsd->strings[igsd->strings_count] = zstr; igsd->strings_count += 1; igsd->buffer_offset++; return zstr; } */ } else if (t == igbinary_type_string16 || t == igbinary_type_object16) { if (IGB_NEEDS_MORE_DATA(igsd, 2)) { zend_error(E_WARNING, "igbinary_unserialize_chararray: end-of-data"); return NULL; } l = igbinary_unserialize16(igsd); } else if (EXPECTED(t == igbinary_type_string32 || t == igbinary_type_object32)) { if (IGB_NEEDS_MORE_DATA(igsd, 4)) { zend_error(E_WARNING, "igbinary_unserialize_chararray: end-of-data"); return NULL; } l = igbinary_unserialize32(igsd); } else if (t == igbinary_type_string64) { return igbinary_unserialize_extremely_long_chararray(igsd); } else { zend_error(E_WARNING, "igbinary_unserialize_chararray: unknown type '%02x', position %zu", t, (size_t)IGB_BUFFER_OFFSET(igsd)); return NULL; } if (IGB_NEEDS_MORE_DATA(igsd, l)) { zend_error(E_WARNING, "igbinary_unserialize_chararray: end-of-data"); return NULL; } if (igsd->strings_count + 1 > igsd->strings_capacity) { zend_string **new_strings; igsd->strings_capacity *= 2; new_strings = (zend_string **)erealloc(igsd->strings, sizeof(zend_string *) * igsd->strings_capacity); if (new_strings == NULL) { // The cleanup function will take care of destroying the allocated zend_strings. return NULL; } igsd->strings = new_strings; } #if PHP_VERSION_ID >= 80100 if (check_interned && l < 100) { /* * Reuse interned strings if possible for the following reasons: * 1. Save memory (e.g. for strings in property default values of classes, for arrays repeating string field names, etc.) * 2. Speed up checking if strings are identical. * 3. Speed up the code that ends up using the return value of igbinary_unserialize(). * * Note that this change has mixed results. unserialize-object-array and unserialize-stdclass have performance improve because all strings are already interned, * but unserialize-stringarray on unstructured text has worse performance. */ zstr = zend_string_init_existing_interned((const char*)igsd->buffer_ptr, l, 0); zend_string_addref(zstr); } else #endif { zstr = zend_string_init((const char*)igsd->buffer_ptr, l, 0); GC_ADDREF(zstr); /* definitely not interned. Add a reference in case the first reference gets deleted before reusing the temporary string */ } igsd->buffer_ptr += l; igsd->strings[igsd->strings_count] = zstr; igsd->strings_count += 1; return zstr; } /* }}} */ /* {{{ igbinary_unserialize_array */ /** Unserializes a PHP array. */ zend_always_inline static int igbinary_unserialize_array(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval *const z, int flags, zend_bool create_ref) { /* WANT_REF means that z will be wrapped by an IS_REFERENCE */ uint32_t n; uint32_t i; enum igbinary_type key_type; HashTable *h; if (t == igbinary_type_array8) { if (IGB_NEEDS_MORE_DATA(igsd, 1)) { zend_error(E_WARNING, "igbinary_unserialize_array: end-of-data"); return 1; } n = igbinary_unserialize8(igsd); if (n == 0) { if (create_ref) { /* NOTE: igbinary uses ZVAL_ARR() when reading the created reference, which assumes that the original array is refcounted. */ /* This would have to be fixed to switch to ZVAL_EMPTY_ARRAY for the empty array (there's probably not benefit to making that switch). */ /* Otherwise, using ZVAL_EMPTY_ARRAY would segfault. */ #if PHP_VERSION_ID >= 70300 ZVAL_EMPTY_ARRAY(z); #else array_init_size(z, 0); #endif struct igbinary_value_ref ref; if (flags & WANT_REF) { ZVAL_NEW_REF(z, z); ref.reference.reference = Z_REF_P(z); ref.type = IG_REF_IS_REFERENCE; } else { #if PHP_VERSION_ID >= 70300 ref.type = IG_REF_IS_EMPTY_ARRAY; #else ref.reference.array = Z_ARR_P(z); ref.type = IG_REF_IS_ARRAY; #endif } /* add the new array to the list of unserialized references */ RETURN_1_IF_NON_ZERO(igsd_append_ref(igsd, ref) == SIZE_MAX); } else { /* This only matters if __unserialize() would get called with an empty array */ #if PHP_VERSION_ID >= 70300 ZVAL_EMPTY_ARRAY(z); #else array_init_size(z, 0); #endif } return 0; } } else if (t == igbinary_type_array16) { if (IGB_NEEDS_MORE_DATA(igsd, 2)) { zend_error(E_WARNING, "igbinary_unserialize_array: end-of-data"); return 1; } n = igbinary_unserialize16(igsd); } else if (t == igbinary_type_array32) { if (IGB_NEEDS_MORE_DATA(igsd, 4)) { zend_error(E_WARNING, "igbinary_unserialize_array: end-of-data"); return 1; } n = igbinary_unserialize32(igsd); } else { zend_error(E_WARNING, "igbinary_unserialize_array: unknown type '%02x', position %zu", t, (size_t)IGB_BUFFER_OFFSET(igsd)); return 1; } /* n cannot be larger than the number of minimum "objects" in the array */ if (IGB_NEEDS_MORE_DATA(igsd, n)) { zend_error(E_WARNING, "igbinary_unserialize_array: data size %zu smaller that requested array length %zu.", (size_t)IGB_REMAINING_BYTES(igsd), (size_t)n); return 1; } zval *z_deref = z; if (flags & WANT_REF) { if (!Z_ISREF_P(z)) { ZVAL_NEW_REF(z, z); z_deref = Z_REFVAL_P(z); } } array_init_size(z_deref, n); h = Z_ARRVAL_P(z_deref); #if PHP_VERSION_ID >= 70200 /* The array may contain references to itself, in which case we'll be modifying an * rc>1 array. This is okay, since the array is, ostensibly, only visible to * unserialize (in practice unserialization handlers also see it). */ HT_ALLOW_COW_VIOLATION(h); #endif if (create_ref) { /* Only create a reference if this is not from __unserialize(), because the existence of __unserialize can change */ struct igbinary_value_ref ref; if (flags & WANT_REF) { // We converted to reference earlier. ref.reference.reference = Z_REF_P(z); ref.type = IG_REF_IS_REFERENCE; } else { ref.reference.array = Z_ARR_P(z_deref); ref.type = IG_REF_IS_ARRAY; } /* add the new array to the list of unserialized references */ RETURN_1_IF_NON_ZERO(igsd_append_ref(igsd, ref) == SIZE_MAX); } for (i = 0; i < n; i++) { zval *vp; zend_long key_index = 0; zend_string *key_str = NULL; /* NULL means use key_index */ if (IGB_NEEDS_MORE_DATA(igsd, 1)) { zend_error(E_WARNING, "igbinary_unserialize_array: end-of-data"); cleanup: zval_dtor(z); ZVAL_NULL(z); return 1; } key_type = (enum igbinary_type)igbinary_unserialize8(igsd); switch (key_type) { case igbinary_type_long8p: /* Manually inline igbinary_unserialize_long() for array keys from 0 to 255, because they're the most common among integers. */ if (IGB_NEEDS_MORE_DATA(igsd, 1)) { zend_error(E_WARNING, "igbinary_unserialize_long: end-of-data"); goto cleanup; } key_index = igbinary_unserialize8(igsd); break; case igbinary_type_long16p: /* and for array keys from 0 to 65535. */ if (IGB_NEEDS_MORE_DATA(igsd, 2)) { zend_error(E_WARNING, "igbinary_unserialize_long: end-of-data"); goto cleanup; } key_index = igbinary_unserialize16(igsd); break; case igbinary_type_long8n: case igbinary_type_long16n: case igbinary_type_long32p: case igbinary_type_long32n: case igbinary_type_long64p: case igbinary_type_long64n: if (UNEXPECTED(igbinary_unserialize_long(igsd, key_type, &key_index))) { goto cleanup; } break; case igbinary_type_string_id8: case igbinary_type_string_id16: case igbinary_type_string_id32: key_str = igbinary_unserialize_string(igsd, key_type); if (UNEXPECTED(key_str == NULL)) { goto cleanup; } break; case igbinary_type_string8: case igbinary_type_string16: case igbinary_type_string32: case igbinary_type_string64: key_str = igbinary_unserialize_chararray(igsd, key_type, 1); if (UNEXPECTED(key_str == NULL)) { goto cleanup; } break; case igbinary_type_string_empty: key_str = ZSTR_EMPTY_ALLOC(); break; case igbinary_type_null: continue; default: zend_error(E_WARNING, "igbinary_unserialize_array: unknown key type '%02x', position %zu", key_type, (size_t)IGB_BUFFER_OFFSET(igsd)); goto cleanup; } /* first add key into array so references can properly and not stack allocated zvals */ /* Use NULL because inserting UNDEF into array does not add a new element */ if (key_str != NULL) { vp = igbinary_zend_hash_add_or_find(h, key_str); /* If there was an old value instead of an inserted IS_NULL zval (unlikely) and that old value was refcounted, then add a reference and defer freeing that reference */ if (UNEXPECTED(igsd_addref_and_defer_dtor(&igsd->deferred_dtor_tracker, vp))) { return 1; } zend_string_release(key_str); } else { /* If there was an old value instead of an inserted IS_NULL zval (unlikely) and that old value was refcounted, then add a reference and defer freeing that reference */ vp = igbinary_zend_hash_index_add_or_find(h, key_index); if (UNEXPECTED(igsd_addref_and_defer_dtor(&igsd->deferred_dtor_tracker, vp))) { return 1; } } ZEND_ASSERT(vp != NULL); if (Z_TYPE_P(vp) == IS_INDIRECT) { /* TODO: In php 8.1+, IS_INDIRECT checks and macros may become unnecessary * due to $GLOBALS being converted to a regular array in https://wiki.php.net/rfc/restrict_globals_usage */ vp = Z_INDIRECT_P(vp); } ZEND_ASSERT(vp != NULL); if (UNEXPECTED(igbinary_unserialize_zval(igsd, vp, WANT_CLEAR))) { return 1; } } return 0; } /* }}} */ /* {{{ igbinary_unserialize_object_properties */ /** Unserializes the array of object properties and adds those to the object z. */ inline static int igbinary_unserialize_object_properties(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval *const z, const zend_class_entry *ce) { /* WANT_REF means that z will be wrapped by an IS_REFERENCE */ uint32_t n; zval v; zval *z_deref; HashTable *h; zend_bool did_extend; if (t == igbinary_type_array8) { if (IGB_NEEDS_MORE_DATA(igsd, 1)) { zend_error(E_WARNING, "igbinary_unserialize_object_properties: end-of-data"); return 1; } n = igbinary_unserialize8(igsd); } else if (t == igbinary_type_array16) { if (IGB_NEEDS_MORE_DATA(igsd, 2)) { zend_error(E_WARNING, "igbinary_unserialize_object_properties: end-of-data"); return 1; } n = igbinary_unserialize16(igsd); } else if (t == igbinary_type_array32) { if (IGB_NEEDS_MORE_DATA(igsd, 4)) { zend_error(E_WARNING, "igbinary_unserialize_object_properties: end-of-data"); return 1; } n = igbinary_unserialize32(igsd); } else { zend_error(E_WARNING, "igbinary_unserialize_object_properties: unknown type '%02x', position %zu", t, (size_t)IGB_BUFFER_OFFSET(igsd)); return 1; } /* n cannot be larger than the number of minimum "objects" in the array */ if (IGB_NEEDS_MORE_DATA(igsd, n)) { zend_error(E_WARNING, "%s: data size %zu smaller that requested array length %zu.", "igbinary_unserialize_object_properties", (size_t)IGB_REMAINING_BYTES(igsd), (size_t)n); return 1; } z_deref = z; ZVAL_DEREF(z_deref); /* empty array */ if (n == 0) { return 0; } h = HASH_OF_OBJECT(z_deref); did_extend = 0; do { n--; zval *vp; enum igbinary_type key_type; zend_string *key_str = NULL; /* NULL means use key_index */ if (IGB_NEEDS_MORE_DATA(igsd, 1)) { zend_error(E_WARNING, "igbinary_unserialize_object_properties: end-of-data"); zval_dtor(z); ZVAL_NULL(z); return 1; } key_type = (enum igbinary_type)igbinary_unserialize8(igsd); switch (key_type) { case igbinary_type_long8p: case igbinary_type_long8n: case igbinary_type_long16p: case igbinary_type_long16n: case igbinary_type_long32p: case igbinary_type_long32n: case igbinary_type_long64p: case igbinary_type_long64n: { zend_long key_index = 0; if (UNEXPECTED(igbinary_unserialize_long(igsd, key_type, &key_index))) { zval_dtor(z); ZVAL_UNDEF(z); return 1; } key_str = zend_long_to_str(key_index); if (UNEXPECTED(key_str == NULL)) { zval_dtor(z); ZVAL_UNDEF(z); return 1; } break; } case igbinary_type_string_id8: case igbinary_type_string_id16: case igbinary_type_string_id32: key_str = igbinary_unserialize_string(igsd, key_type); if (UNEXPECTED(key_str == NULL)) { zval_dtor(z); ZVAL_UNDEF(z); return 1; } break; case igbinary_type_string8: case igbinary_type_string16: case igbinary_type_string32: case igbinary_type_string64: key_str = igbinary_unserialize_chararray(igsd, key_type, 1); if (UNEXPECTED(key_str == NULL)) { zval_dtor(z); ZVAL_UNDEF(z); return 1; } break; case igbinary_type_string_empty: key_str = ZSTR_EMPTY_ALLOC(); break; case igbinary_type_null: continue; /* Skip unserializing this element, serialized with no value. In C, this applies to loop, not switch. */ default: zend_error(E_WARNING, "igbinary_unserialize_object_properties: unknown key type '%02x', position %zu", key_type, (size_t)IGB_BUFFER_OFFSET(igsd)); zval_dtor(z); ZVAL_UNDEF(z); return 1; } /* first add key into array so references can properly and not stack allocated zvals */ /* Use NULL because inserting UNDEF into array does not add a new element */ ZVAL_NULL(&v); zval *prototype_value = zend_hash_find(h, key_str); #if PHP_VERSION_ID >= 70400 zend_property_info *info = NULL; #endif if (prototype_value != NULL) { if (Z_TYPE_P(prototype_value) == IS_INDIRECT) { /* This is a declared object property */ prototype_value = Z_INDIRECT_P(prototype_value); #if PHP_VERSION_ID >= 70400 info = zend_get_typed_property_info_for_slot(Z_OBJ_P(z_deref), prototype_value); if (info) { if (Z_ISREF_P(prototype_value)) { /* If the value is overwritten, remove old type source from ref. */ ZEND_REF_DEL_TYPE_SOURCE(Z_REF_P(prototype_value), info); } if (igsd->ref_props) { /* Remove old entry from ref_props table, if it exists. */ zend_hash_index_del( igsd->ref_props, ((zend_uintptr_t) prototype_value) >> ZEND_MM_ALIGNMENT_LOG2); } } #endif } /* This is written to avoid the overhead of a second zend_hash_update call. See https://github.com/php/php-src/pull/5095 */ /* Use igsd_addref_and_defer_dtor instead (like php-src), in case of gc causing issues. */ /* Something already added a reference, so just defer the destructor */ if (UNEXPECTED(igsd_defer_dtor(&igsd->deferred_dtor_tracker, prototype_value))) { zend_string_release(key_str); return 1; } /* Just override the original value directly */ ZVAL_COPY_VALUE(prototype_value, &v); vp = prototype_value; } else { #if PHP_VERSION_ID >= 80000 /* ZEND_ACC_NO_DYNAMIC_PROPERTIES was introduced in php 8.0 but was largely used with zend_class_unserialize_deny, though external pecls might set this flag. */ if (UNEXPECTED(ce->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) { zend_throw_error(NULL, "Cannot create dynamic property %s::$%s in igbinary_unserialize", ZSTR_VAL(ce->name), zend_get_unmangled_property_name(key_str)); zend_string_release(key_str); return 1; } #endif #if PHP_VERSION_ID >= 80200 /* PHP 8.2 deprecated the creation of dynamic properties by default without `#[AllowDynamicProperties]`. */ if (UNEXPECTED(!(ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES))) { php_error_docref(NULL, E_DEPRECATED, "Creation of dynamic property %s::$%s is deprecated", ZSTR_VAL(ce->name), zend_get_unmangled_property_name(key_str)); if (UNEXPECTED(EG(exception))) { zend_string_release(key_str); return 1; } } #endif if (!did_extend) { /* remaining_elements is at least one, because we're looping from n-1..0 */ uint32_t remaining_elements = n + 1; /* Copied from var_unserializer.re. Need to ensure that IGB_REF_VAL doesn't point to invalid data. */ /* Worst case: All remaining_elements of the added properties are dynamic. */ zend_hash_extend(h, zend_hash_num_elements(h) + remaining_elements, (h->u.flags & HASH_FLAG_PACKED)); did_extend = 1; } vp = zend_hash_add_new(h, key_str, &v); } zend_string_release(key_str); /* Should only be indirect for typed properties? */ ZEND_ASSERT(Z_TYPE_P(vp) != IS_INDIRECT); if (UNEXPECTED(igbinary_unserialize_zval(igsd, vp, WANT_CLEAR))) { #if PHP_VERSION_ID >= 70400 if (info && Z_ISREF_P(vp)) { /* Add type source even if we failed to unserialize. * The data is still stored in the property. */ ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(vp), info); } #endif /* Unserializing a property into this zval has failed. */ /* zval_ptr_dtor(z); */ /* zval_ptr_dtor(vp); */ return 1; } #if PHP_VERSION_ID >= 70400 if (UNEXPECTED(info)) { if (!zend_verify_prop_assignable_by_ref(info, vp, /* strict */ 1)) { zval_ptr_dtor(vp); ZVAL_UNDEF(vp); return 1; } if (Z_ISREF_P(vp)) { ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(vp), info); } else { /* Remember to which property this slot belongs, so we can add a * type source if it is turned into a reference lateron. */ if (!igsd->ref_props) { igsd->ref_props = emalloc(sizeof(HashTable)); zend_hash_init(igsd->ref_props, 8, NULL, NULL, 0); } zend_hash_index_update_ptr( igsd->ref_props, ((zend_uintptr_t) vp) >> ZEND_MM_ALIGNMENT_LOG2, info); } } #endif } while (n > 0); return 0; } /* }}} */ /* {{{ igbinary_unserialize_object_ser */ /** Unserializes object's property array. This is used to serialize objects implementing Serializable -interface. */ static ZEND_COLD int igbinary_unserialize_object_ser(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval *const z, zend_class_entry *ce) { size_t n; int ret; php_unserialize_data_t var_hash; if (ce->unserialize == NULL) { /* Should be impossible. */ zend_error(E_WARNING, "Class %s has no unserializer", ZSTR_VAL(ce->name)); return 1; } if (IGBINARY_IS_NOT_UNSERIALIZABLE(ce)) { zend_throw_exception_ex(NULL, 0, "Unserialization of '%s' is not allowed", ZSTR_VAL(ce->name)); return 1; } if (t == igbinary_type_object_ser8) { if (IGB_NEEDS_MORE_DATA(igsd, 1)) { zend_error(E_WARNING, "igbinary_unserialize_object_ser: end-of-data"); return 1; } n = igbinary_unserialize8(igsd); } else if (t == igbinary_type_object_ser16) { if (IGB_NEEDS_MORE_DATA(igsd, 2)) { zend_error(E_WARNING, "igbinary_unserialize_object_ser: end-of-data"); return 1; } n = igbinary_unserialize16(igsd); } else if (t == igbinary_type_object_ser32) { if (IGB_NEEDS_MORE_DATA(igsd, 4)) { zend_error(E_WARNING, "igbinary_unserialize_object_ser: end-of-data"); return 1; } n = igbinary_unserialize32(igsd); } else { zend_error(E_WARNING, "igbinary_unserialize_object_ser: unknown type '%02x', position %zu", t, (size_t)IGB_BUFFER_OFFSET(igsd)); return 1; } if (IGB_NEEDS_MORE_DATA(igsd, n)) { zend_error(E_WARNING, "igbinary_unserialize_object_ser: end-of-data"); return 1; } PHP_VAR_UNSERIALIZE_INIT(var_hash); ret = ce->unserialize( z, ce, (const unsigned char *)igsd->buffer_ptr, n, (zend_unserialize_data *)&var_hash ); PHP_VAR_UNSERIALIZE_DESTROY(var_hash); if (ret != SUCCESS || EG(exception)) { return 1; } igsd->buffer_ptr += n; return 0; } /* }}} */ /* {{{ igbinary_unserialize_object_enum_case */ #if PHP_VERSION_ID >= 80100 /** Unserializes object's property array. This is used to serialize objects implementing Serializable -interface. */ static int igbinary_unserialize_object_enum_case(struct igbinary_unserialize_data *igsd, zval *const z, zend_class_entry *ce) { if (UNEXPECTED(!(ce->ce_flags & ZEND_ACC_ENUM))) { zend_error(E_WARNING, "igbinary_unserialize_object_enum_case: Class '%s' is not an enum", ZSTR_VAL(ce->name)); return 1; } if (IGB_NEEDS_MORE_DATA(igsd, 1)) { zend_error(E_WARNING, "igbinary_unserialize_object_enum_case: end-of-data"); return 1; } enum igbinary_type t = (enum igbinary_type)igbinary_unserialize8(igsd); zend_string *case_name; switch (t) { case igbinary_type_string8: case igbinary_type_string16: case igbinary_type_string32: case igbinary_type_string64: case_name = igbinary_unserialize_chararray(igsd, t, 1); break; default: case_name = igbinary_unserialize_string(igsd, t); break; } if (UNEXPECTED(!case_name)) { return 1; } zval *zv = zend_hash_find(&ce->constants_table, case_name); if (UNEXPECTED(!zv)) { zend_error(E_WARNING, "igbinary_unserialize_object_enum_case: Undefined constant %s::%s", ZSTR_VAL(ce->name), ZSTR_VAL(case_name)); zend_string_release(case_name); return 1; } zend_class_constant *c = Z_PTR_P(zv); if (UNEXPECTED(!(ZEND_CLASS_CONST_FLAGS(c) & ZEND_CLASS_CONST_IS_CASE))) { zend_error(E_WARNING, "igbinary_unserialize_object_enum_case: %s::%s is not an enum case", ZSTR_VAL(ce->name), ZSTR_VAL(case_name)); zend_string_release(case_name); return 1; } zend_string_release(case_name); zval *value = &c->value; if (Z_TYPE_P(value) == IS_CONSTANT_AST) { zval_update_constant_ex(value, c->ce); if (UNEXPECTED(EG(exception) != NULL)) { return 1; } } ZEND_ASSERT(Z_TYPE_P(value) == IS_OBJECT); /* increment the reference count and copy the enum case object into the constructed value. */ ZVAL_COPY(z, value); return 0; } #endif /* }}} */ /* {{{ igbinary_unserialize_object */ /** Unserialize an object. * @see ext/standard/var_unserializer.c in the php-src repo. Parts of this code are based on that. */ zend_always_inline static int igbinary_unserialize_object(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval *z, int flags) { zend_class_entry *ce; size_t ref_n; zend_string *class_name; int r; bool incomplete_class = false; bool is_from_serialized_data = false; if (t == igbinary_type_object8 || t == igbinary_type_object16 || t == igbinary_type_object32) { class_name = igbinary_unserialize_chararray(igsd, t, 1); } else if (t == igbinary_type_object_id8 || t == igbinary_type_object_id16 || t == igbinary_type_object_id32) { class_name = igbinary_unserialize_string(igsd, t); } else { zend_error(E_WARNING, "igbinary_unserialize_object: unknown object type '%02x', position %zu", t, (size_t)IGB_BUFFER_OFFSET(igsd)); return 1; } if (class_name == NULL) { return 1; } do { zval user_func; zval retval; zval args[1]; const char* user_func_name; /* Try to find class directly */ if (EXPECTED((ce = zend_lookup_class(class_name)) != NULL)) { /* FIXME: lookup class may cause exception in load callback */ break; } user_func_name = PG(unserialize_callback_func); /* Check for unserialize callback */ if ((user_func_name == NULL) || (user_func_name[0] == '\0')) { incomplete_class = 1; ce = PHP_IC_ENTRY; break; } /* Call unserialize callback */ ZVAL_STRING(&user_func, user_func_name); ZVAL_STR(&args[0], class_name); if (call_user_function(CG(function_table), NULL, &user_func, &retval, 1, args) != SUCCESS) { php_error_docref(NULL, E_WARNING, "defined (%s) but not found", Z_STRVAL(user_func)); incomplete_class = 1; ce = PHP_IC_ENTRY; zval_ptr_dtor_nogc(&user_func); break; } /* FIXME: always safe? */ zval_ptr_dtor(&retval); zval_ptr_dtor_str(&user_func); /* User function call may have raised an exception */ if (EG(exception)) { zend_string_release_ex(class_name, 0); return 1; } /* The callback function may have defined the class */ ce = zend_lookup_class(class_name); if (!ce) { php_error_docref(NULL, E_WARNING, "Function %s() hasn't defined the class it was called for", PG(unserialize_callback_func)); incomplete_class = true; ce = PHP_IC_ENTRY; } } while (0); if (IGBINARY_IS_NOT_UNSERIALIZABLE(ce)) { zend_throw_exception_ex(NULL, 0, "Unserialization of '%s' is not allowed", ZSTR_VAL(ce->name)); zend_string_release(class_name); return 1; } /* add this to the list of unserialized references, get the index */ if (IGB_NEEDS_MORE_DATA(igsd, 1)) { zend_error(E_WARNING, "igbinary_unserialize_object: end-of-data"); zend_string_release(class_name); return 1; } { /* The actual value of ref is unused. We use ref_n later in this function, after creating the object. */ struct igbinary_value_ref ref = {{0}, 0}; ref_n = igsd_append_ref(igsd, ref); if (UNEXPECTED(ref_n == SIZE_MAX)) { zend_string_release(class_name); return 1; } } t = (enum igbinary_type)igbinary_unserialize8(igsd); switch (t) { case igbinary_type_array8: case igbinary_type_array16: case igbinary_type_array32: { if (UNEXPECTED(object_init_ex(z, ce) != SUCCESS)) { php_error_docref(NULL, E_NOTICE, "igbinary unable to create object for class entry"); r = 1; break; } if (incomplete_class) { #if PHP_VERSION_ID >= 80000 php_store_class_name(z, class_name); #else php_store_class_name(z, ZSTR_VAL(class_name), ZSTR_LEN(class_name)); #endif #if PHP_VERSION_ID >= 70400 } else { #if PHP_VERSION_ID >= 80000 if (ce->__unserialize) #else if (zend_hash_str_exists(&ce->function_table, "__unserialize", sizeof("__unserialize") - 1)) #endif { ZEND_ASSERT(Z_TYPE_P(z) == IS_OBJECT); struct igbinary_value_ref *ref = &IGB_REF_VAL_2(igsd, ref_n); if ((flags & WANT_REF) != 0) { ZVAL_MAKE_REF(z); ref->reference.reference = Z_REF_P(z); ref->type = IG_REF_IS_REFERENCE; } else { ref->reference.object = Z_OBJ_P(z); ref->type = IG_REF_IS_OBJECT; } /* Unserialize the array as an array for a deferred call to __unserialize */ zval param; int result; zend_string_release(class_name); result = igbinary_unserialize_array(igsd, t, ¶m, 0, false); ZVAL_DEREF(z); ZEND_ASSERT(Z_TYPE_P(z) == IS_OBJECT); igsd_defer_unserialize(igsd, Z_OBJ_P(z), param); return result; } #endif } struct igbinary_value_ref *ref = &IGB_REF_VAL_2(igsd, ref_n); if ((flags & WANT_REF) != 0) { ZVAL_MAKE_REF(z); ref->reference.reference = Z_REF_P(z); ref->type = IG_REF_IS_REFERENCE; } else { ref->reference.object = Z_OBJ_P(z); ref->type = IG_REF_IS_OBJECT; } r = igbinary_unserialize_object_properties(igsd, t, z, ce); break; } case igbinary_type_object_ser8: case igbinary_type_object_ser16: case igbinary_type_object_ser32: { is_from_serialized_data = true; /* FIXME will this break if z isn't an object? */ r = igbinary_unserialize_object_ser(igsd, t, z, ce); if (r != 0) { break; } if (incomplete_class) { #if PHP_VERSION_ID >= 80000 php_store_class_name(z, class_name); #else php_store_class_name(z, ZSTR_VAL(class_name), ZSTR_LEN(class_name)); #endif } struct igbinary_value_ref *ref = &IGB_REF_VAL_2(igsd, ref_n); if ((flags & WANT_REF) != 0) { ZVAL_MAKE_REF(z); ref->reference.reference = Z_REF_P(z); ref->type = IG_REF_IS_REFERENCE; } else { ref->reference.object = Z_OBJ_P(z); ref->type = IG_REF_IS_OBJECT; } break; } case igbinary_type_enum_case: #if PHP_VERSION_ID >= 80100 if (UNEXPECTED(incomplete_class)) { zend_error(E_WARNING, "igbinary_unserialize_object_enum_case: Class '%s' does not exist", ZSTR_VAL(class_name)); zend_string_release(class_name); return 1; } zend_string_release(class_name); r = igbinary_unserialize_object_enum_case(igsd, z, ce); if (r) { return r; } struct igbinary_value_ref *ref = &IGB_REF_VAL_2(igsd, ref_n); if ((flags & WANT_REF) != 0) { ZVAL_MAKE_REF(z); ref->reference.reference = Z_REF_P(z); ref->type = IG_REF_IS_REFERENCE; } else { ref->reference.object = Z_OBJ_P(z); ref->type = IG_REF_IS_OBJECT; } return 0; #else zend_error(E_WARNING, "igbinary_unserialize_object: Cannot unserialize enum cases prior to php 8.1 at position %zu", (size_t)IGB_BUFFER_OFFSET(igsd)); r = 1; #endif break; default: zend_error(E_WARNING, "igbinary_unserialize_object: unknown object inner type '%02x', position %zu", t, (size_t)IGB_BUFFER_OFFSET(igsd)); r = 1; } zend_string_release(class_name); class_name = NULL; /* If unserialize was successful, defer the call to __wakeup if __wakeup exists for this object. */ /* (But don't call __wakeup() if Serializable::unserialize was called */ if (r == 0 && !is_from_serialized_data) { struct igbinary_value_ref *const ref = &IGB_REF_VAL_2(igsd, ref_n); zend_object *object; if (ref->type == IG_REF_IS_OBJECT) { object = ref->reference.object; } else if (EXPECTED(ref->type == IG_REF_IS_REFERENCE)) { /* May have created a reference while deserializing an object, if it was recursive. */ zval ztemp = ref->reference.reference->val; if (UNEXPECTED(Z_TYPE(ztemp) != IS_OBJECT)) { zend_error(E_WARNING, "igbinary_unserialize_object preparing to __wakeup/__unserialize: got reference to non-object somehow (inner type '%02x', position %zu)", t, (size_t)IGB_BUFFER_OFFSET(igsd)); return 1; } object = Z_OBJ(ztemp); } else { zend_error(E_WARNING, "igbinary_unserialize_object preparing to __wakeup/__unserialize: created non-object somehow (inner type '%02x', position %zu)", t, (size_t)IGB_BUFFER_OFFSET(igsd)); return 1; } if (object->ce != PHP_IC_ENTRY) { if (zend_hash_str_exists(&object->ce->function_table, "__wakeup", sizeof("__wakeup") - 1)) { if (UNEXPECTED(igsd_defer_wakeup(igsd, object))) { return 1; } } } } return r; } /* }}} */ /* {{{ igbinary_unserialize_ref */ /** Unserializes an array or object by reference. */ zend_always_inline static int igbinary_unserialize_ref(struct igbinary_unserialize_data *igsd, enum igbinary_type t, zval *const z, int flags) { size_t n; if (t == igbinary_type_ref8 || t == igbinary_type_objref8) { if (IGB_NEEDS_MORE_DATA(igsd, 1)) { zend_error(E_WARNING, "igbinary_unserialize_ref: end-of-data"); return 1; } n = igbinary_unserialize8(igsd); } else if (t == igbinary_type_ref16 || t == igbinary_type_objref16) { if (IGB_NEEDS_MORE_DATA(igsd, 2)) { zend_error(E_WARNING, "igbinary_unserialize_ref: end-of-data"); return 1; } n = igbinary_unserialize16(igsd); } else if (t == igbinary_type_ref32 || t == igbinary_type_objref32) { if (IGB_NEEDS_MORE_DATA(igsd, 4)) { zend_error(E_WARNING, "igbinary_unserialize_ref: end-of-data"); return 1; } n = igbinary_unserialize32(igsd); } else { zend_error(E_WARNING, "igbinary_unserialize_ref: unknown type '%02x', position %zu", t, (size_t)IGB_BUFFER_OFFSET(igsd)); return 1; } if (n >= igsd->references_count) { zend_error(E_WARNING, "igbinary_unserialize_ref: invalid reference %zu >= %zu", (size_t)n, (size_t)igsd->references_count); return 1; } if (z != NULL) { /* FIXME: check with is refcountable or some such */ zval_ptr_dtor(z); ZVAL_UNDEF(z); } struct igbinary_value_ref *ref_ptr = &IGB_REF_VAL_2(igsd, n); struct igbinary_value_ref ref = *ref_ptr; /** * Permanently convert the zval in IGB_REF_VAL() into a IS_REFERENCE if it wasn't already one. * TODO: Can there properly be multiple reference groups to an object? * Similar to https://github.com/php/php-src/blob/master/ext/standard/var_unserializer.re , for "R:" * Using `flags` because igbinary_unserialize_ref might be used both for copy on writes ($a = $b = [2]) and by PHP references($a = &$b). */ if ((flags & WANT_REF) != 0) { /* Want to create an IS_REFERENCE, not just to share the same value until modified. */ switch (ref.type) { case IG_REF_IS_OBJECT: ZVAL_OBJ(z, ref.reference.object); Z_ADDREF_P(z); ZVAL_MAKE_REF(z); /* Convert original zval data to a reference */ /* replace the entry in IGB_REF_VAL with a reference. */ ref_ptr->reference.reference = Z_REF_P(z); ref_ptr->type = IG_REF_IS_REFERENCE; break; case IG_REF_IS_ARRAY: ZVAL_ARR(z, ref.reference.array); /* All arrays built by igbinary when unserializing are refcounted, except IG_REF_IS_EMPTY_ARRAY. */ /* If they were not refcounted, the ZVAL_ARR call would probably also need to be changed. */ Z_ADDREF_P(z); ZVAL_MAKE_REF(z); /* Convert original zval data to a reference */ /* replace the entry in IGB_REF_VAL with a reference. */ ref_ptr->reference.reference = Z_REF_P(z); ref_ptr->type = IG_REF_IS_REFERENCE; break; #if PHP_VERSION_ID >= 70300 case IG_REF_IS_EMPTY_ARRAY: ZVAL_EMPTY_ARRAY(z); ZVAL_MAKE_REF(z); /* Convert original zval data to a reference */ /* replace the entry in IGB_REF_VAL with a reference. */ ref_ptr->reference.reference = Z_REF_P(z); ref_ptr->type = IG_REF_IS_REFERENCE; break; #endif case IG_REF_IS_REFERENCE: // This is already a reference, convert into reference count. ZVAL_REF(z, ref.reference.reference); Z_ADDREF_P(z); break; } } else { switch (ref.type) { case IG_REF_IS_OBJECT: ZVAL_OBJ(z, ref.reference.object); Z_ADDREF_P(z); break; case IG_REF_IS_ARRAY: ZVAL_ARR(z, ref.reference.array); Z_ADDREF_P(z); break; #if PHP_VERSION_ID >= 70300 case IG_REF_IS_EMPTY_ARRAY: ZVAL_EMPTY_ARRAY(z); break; #endif case IG_REF_IS_REFERENCE: ZVAL_COPY(z, &(ref.reference.reference->val)); break; } } return 0; } /* }}} */ /* {{{ igbinary_unserialize_zval */ /** Unserialize a zval of any serializable type (zval is PHP's internal representation of a value). */ static int igbinary_unserialize_zval(struct igbinary_unserialize_data *igsd, zval *const z, int flags) { enum igbinary_type t; zend_long tmp_long; double tmp_double; zend_string *tmp_str; if (IGB_NEEDS_MORE_DATA(igsd, 1)) { zend_error(E_WARNING, "igbinary_unserialize_zval: end-of-data"); return 1; } t = (enum igbinary_type)igbinary_unserialize8(igsd); switch (t) { case igbinary_type_ref: if (UNEXPECTED(igbinary_unserialize_zval(igsd, z, WANT_REF))) { return 1; } /* If it is already a ref, nothing to do */ if (Z_ISREF_P(z)) { break; } const zend_uchar type = Z_TYPE_P(z); /* Permanently convert the zval in IGB_REF_VAL() into a IS_REFERENCE if it wasn't already one. */ /* TODO: Support multiple reference groups to the same object */ /* Similar to https://github.com/php/php-src/blob/master/ext/standard/var_unserializer.re , for "R:" */ if (!Z_ISREF_P(z)) { #if PHP_VERSION_ID >= 70400 zend_property_info *info = NULL; if (igsd->ref_props) { info = zend_hash_index_find_ptr(igsd->ref_props, ((zend_uintptr_t) z) >> ZEND_MM_ALIGNMENT_LOG2); } #endif ZVAL_NEW_REF(z, z); #if PHP_VERSION_ID >= 70400 if (info) { ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(z), info); } #endif } switch (type) { case IS_STRING: case IS_LONG: case IS_NULL: case IS_DOUBLE: case IS_FALSE: case IS_TRUE: { struct igbinary_value_ref ref; ref.reference.reference = Z_REF_P(z); ref.type = IG_REF_IS_REFERENCE; /* add the unserialized scalar to the list of unserialized references. Objects and arrays were already added in igbinary_unserialize_zval. */ RETURN_1_IF_NON_ZERO(igsd_append_ref(igsd, ref) == SIZE_MAX); break; } default: break; } break; case igbinary_type_objref8: case igbinary_type_objref16: case igbinary_type_objref32: case igbinary_type_ref8: case igbinary_type_ref16: case igbinary_type_ref32: if (UNEXPECTED(igbinary_unserialize_ref(igsd, t, z, flags))) { return 1; } break; case igbinary_type_object8: case igbinary_type_object16: case igbinary_type_object32: case igbinary_type_object_id8: case igbinary_type_object_id16: case igbinary_type_object_id32: if (UNEXPECTED(igbinary_unserialize_object(igsd, t, z, flags))) { return 1; } break; case igbinary_type_array8: case igbinary_type_array16: case igbinary_type_array32: if (UNEXPECTED(igbinary_unserialize_array(igsd, t, z, flags, true))) { return 1; } break; case igbinary_type_string_empty: ZVAL_EMPTY_STRING(z); break; case igbinary_type_string_id8: case igbinary_type_string_id16: case igbinary_type_string_id32: tmp_str = igbinary_unserialize_string(igsd, t); if (UNEXPECTED(tmp_str == NULL)) { return 1; } ZVAL_STR(z, tmp_str); break; case igbinary_type_string8: case igbinary_type_string16: case igbinary_type_string32: case igbinary_type_string64: tmp_str = igbinary_unserialize_chararray(igsd, t, 0); if (UNEXPECTED(tmp_str == NULL)) { return 1; } ZVAL_STR(z, tmp_str); break; case igbinary_type_long8p: /* Manually inline igbinary_unserialize_long() for values from 0 to 255, because they're the most common among integers in many applications. */ if (IGB_NEEDS_MORE_DATA(igsd, 1)) { zend_error(E_WARNING, "igbinary_unserialize_long: end-of-data"); return 1; } ZVAL_LONG(z, igbinary_unserialize8(igsd)); break; case igbinary_type_long16p: /* Manually inline igbinary_unserialize_long() for values from 0 to 255, because they're the most common among integers in many applications. */ if (IGB_NEEDS_MORE_DATA(igsd, 2)) { zend_error(E_WARNING, "igbinary_unserialize_long: end-of-data"); return 1; } ZVAL_LONG(z, igbinary_unserialize16(igsd)); break; case igbinary_type_long8n: case igbinary_type_long16n: case igbinary_type_long32p: case igbinary_type_long32n: case igbinary_type_long64p: case igbinary_type_long64n: if (UNEXPECTED(igbinary_unserialize_long(igsd, t, &tmp_long))) { return 1; } ZVAL_LONG(z, tmp_long); break; case igbinary_type_null: ZVAL_NULL(z); break; case igbinary_type_bool_false: ZVAL_BOOL(z, 0); break; case igbinary_type_bool_true: ZVAL_BOOL(z, 1); break; case igbinary_type_double: if (UNEXPECTED(igbinary_unserialize_double(igsd, &tmp_double))) { return 1; } ZVAL_DOUBLE(z, tmp_double); break; default: zend_error(E_WARNING, "igbinary_unserialize_zval: unknown type '%02x', position %zu", t, (size_t)IGB_BUFFER_OFFSET(igsd)); return 1; } return 0; } /* }}} */ /* * Local variables: * tab-width: 2 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ igbinary-3.2.13/src/php7/igbinary.h0000664000175000017500000000646514366750307016170 0ustar tysontyson/* +----------------------------------------------------------------------+ | See COPYING file for further copyright information | +----------------------------------------------------------------------+ | Author: Oleg Grenrus | | See CREDITS for contributors | +----------------------------------------------------------------------+ */ #ifndef IGBINARY_H #define IGBINARY_H #include /* Forward declarations. */ struct zval; /* Constants and constant macros */ /** Binary protocol version of igbinary. */ #define IGBINARY_FORMAT_VERSION 0x00000002 #define PHP_IGBINARY_VERSION "3.2.13" /* Macros */ #ifdef PHP_WIN32 # if defined(IGBINARY_EXPORTS) || (!defined(COMPILE_DL_IGBINARY)) # define IGBINARY_API __declspec(dllexport) # elif defined(COMPILE_DL_IGBINARY) # define IGBINARY_API __declspec(dllimport) # else # define IGBINARY_API /* nothing special */ # endif #elif defined(__GNUC__) && __GNUC__ >= 4 # define IGBINARY_API __attribute__ ((visibility("default"))) #else # define IGBINARY_API /* nothing special */ #endif /** Struct that contains pointers to memory allocation and deallocation functions. * @see igbinary_serialize_data */ struct igbinary_memory_manager { void *(*alloc)(size_t size, void *context); void *(*realloc)(void *ptr, size_t new_size, void *context); void (*free)(void *ptr, void *context); void *context; }; /** Serialize zval. * Return buffer is allocated by this function with emalloc. * @param[out] ret Return buffer * @param[out] ret_len Size of return buffer * @param[in] z Variable to be serialized * @return 0 on success, 1 elsewhere. */ IGBINARY_API int igbinary_serialize(uint8_t **ret, size_t *ret_len, zval *z); /** Serialize zval. * Return buffer is allocated by this function with emalloc. * @param[out] ret Return buffer * @param[out] ret_len Size of return buffer * @param[in] z Variable to be serialized * @param[in] memory_manager Pointer to the structure that contains memory allocation functions. * @return 0 on success, 1 elsewhere. */ IGBINARY_API int igbinary_serialize_ex(uint8_t **ret, size_t *ret_len, zval *z, struct igbinary_memory_manager *memory_manager); /** Unserialize to zval. * @param[in] buf Buffer with serialized data. * @param[in] buf_len Buffer length. * @param[out] z Unserialized zval * @return 0 on success, 1 elsewhere. */ IGBINARY_API int igbinary_unserialize(const uint8_t *buf, size_t buf_len, zval *z); static zend_always_inline int _igbinary_has_valid_header(const uint8_t *buf, size_t buf_len) { if (buf_len < 5) { /* Must have 4 header bytes and at least one byte of data */ return 0; } /* Unserialize 32bit value the same way on big-endian and little-endian architectures. * This compiles to a load+optional bswap when compiler optimizations are enabled. */ const uint32_t ret = ((uint32_t)(buf[0]) << 24) | ((uint32_t)(buf[1]) << 16) | ((uint32_t)(buf[2]) << 8) | ((uint32_t)(buf[3])); return ret == 1 || ret == 2; } /** This is defined as a macro and a static C function * to allow callers to use the macro from newer igbinary versions even with older igbinary installations. */ #define igbinary_has_valid_header(buf, buf_len) _igbinary_has_valid_header((buf), (buf_len)) #endif /* IGBINARY_H */ igbinary-3.2.13/src/php7/igbinary_bswap.h0000664000175000017500000000427714366750307017363 0ustar tysontyson#ifndef IGBINARY_BSWAP #define IGBINARY_BSWAP #ifdef _MSC_VER // bswap compiler checks copied from https://github.com/google/cityhash/blob/8af9b8c2b889d80c22d6bc26ba0df1afb79a30db/src/city.cc#L50 // The original code for detecting bswap had the MIT License below. // // Copyright (c) 2011 Google, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #include #define bswap_32(x) _byteswap_ulong(x) #define bswap_64(x) _byteswap_uint64(x) #elif defined(__APPLE__) // Mac OS X / Darwin features #include #define bswap_32(x) OSSwapInt32(x) #define bswap_64(x) OSSwapInt64(x) #elif defined(__sun) || defined(sun) #include #define bswap_32(x) BSWAP_32(x) #define bswap_64(x) BSWAP_64(x) #elif defined(__FreeBSD__) #include #define bswap_32(x) bswap32(x) #define bswap_64(x) bswap64(x) #elif defined(__OpenBSD__) #include #define bswap_32(x) swap32(x) #define bswap_64(x) swap64(x) #elif defined(__NetBSD__) #include #include #if defined(__BSWAP_RENAME) && !defined(__bswap_32) #define bswap_32(x) bswap32(x) #define bswap_64(x) bswap64(x) #endif #else #include #endif #endif igbinary-3.2.13/src/php7/igbinary_macros.h0000664000175000017500000000117214366750307017522 0ustar tysontyson/* +----------------------------------------------------------------------+ | See COPYING file for further copyright information | +----------------------------------------------------------------------+ | See CREDITS for contributors | +----------------------------------------------------------------------+ */ // If a macro is needed by *only* igbinary, put it in this file. #ifndef PHP_IGBINARY_MACROS_H #define PHP_IGBINARY_MACROS_H /** Backport macros from php 7.3 */ #ifndef GC_ADD_FLAGS #define GC_ADD_FLAGS(obj, flag) GC_FLAGS(obj) |= flag #endif #endif igbinary-3.2.13/src/php7/igbinary_zend_hash.h0000664000175000017500000002557514366750307020216 0ustar tysontyson#ifndef IGBINARY_ZEND_HASH #define IGBINARY_ZEND_HASH /* +----------------------------------------------------------------------+ | igbinary optimizations for find_or_insert adapted from Zend Engine. | | Adaptations written by Tyson Andre for igbinary. | | original license of php-src/Zend/zend_hash.c is below. | +----------------------------------------------------------------------+ | Zend Engine | +----------------------------------------------------------------------+ | Copyright (c) 1998-2018 Zend Technologies Ltd. (http://www.zend.com) | +----------------------------------------------------------------------+ | This source file is subject to version 2.00 of the Zend license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.zend.com/license/2_00.txt. | | If you did not receive a copy of the Zend license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@zend.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Andi Gutmans | | Zeev Suraski | | Dmitry Stogov | +----------------------------------------------------------------------+ */ /* See https://nikic.github.io/2014/12/22/PHPs-new-hashtable-implementation.html for an overview of PHP's hash table information */ #if PHP_VERSION_ID < 70200 || PHP_VERSION_ID >= 80100 || IGBINARY_FORCE_REFERENCE_HASH_TABLE_FUNCTIONS /* {{{ igbinary_zend_hash_add_or_find(HashTable *ht, zend_string *key) unoptimized reference implementation. */ /* This will either return the pointer to the existing value, or add a brand new entry to the hash table with a PHP IS_NULL value. */ static zend_always_inline zval *igbinary_zend_hash_add_or_find(HashTable *ht, zend_string *key) { zval *vp; zval val; if (UNEXPECTED((vp = zend_hash_find(ht, key)) != NULL)) { return vp; } ZVAL_NULL(&val); return zend_hash_add_new(ht, key, &val); } /* }}} */ /* {{{ igbinary_zend_hash_add_or_find(HashTable *ht, zend_long key) unoptimized reference implementation. */ /* This will either return the pointer to the existing value, or add a brand new entry to the hash table with a PHP IS_NULL value. */ static zend_always_inline zval *igbinary_zend_hash_index_add_or_find(HashTable *ht, zend_long key) { zval *vp; zval val; if (UNEXPECTED((vp = zend_hash_index_find(ht, key)) != NULL)) { return vp; } ZVAL_NULL(&val); return zend_hash_index_add_new(ht, key, &val); } /* }}} */ /* This is either too old to bother implementing specializations for, or too new for the HashTable implementation to be finalized and tested */ #else /* PHP 7.2 to 8.0 */ #if PHP_VERSION_ID < 70300 # define IS_ARRAY_PERSISTENT HASH_FLAG_PERSISTENT # define zend_hash_real_init_mixed(ht) zend_hash_real_init((ht), 0) # define zend_hash_real_init_packed(ht) zend_hash_real_init((ht), 1) # define HT_SIZE_TO_MASK(size) (-(size)) # define HT_FLAGS(ht) ((ht)->u.flags) static zend_always_inline zend_bool zend_string_equal_content(zend_string *s1, zend_string *s2) { return ZSTR_LEN(s1) == ZSTR_LEN(s2) && !memcmp(ZSTR_VAL(s1), ZSTR_VAL(s2), ZSTR_LEN(s1)); } #endif /* PHP_VERSION_ID < 70300 */ /* {{{ igbinary_zend_hash_find_bucket(const HashTable *ht, zend_string *key, zend_bool known_hash) */ static zend_always_inline Bucket *igbinary_zend_hash_find_bucket(const HashTable *ht, zend_string *key, zend_bool known_hash) { zend_ulong h; uint32_t nIndex; uint32_t idx; Bucket *p, *arData; if (known_hash) { h = ZSTR_H(key); } else { h = zend_string_hash_val(key); } arData = ht->arData; nIndex = h | ht->nTableMask; idx = HT_HASH_EX(arData, nIndex); if (UNEXPECTED(idx == HT_INVALID_IDX)) { return NULL; } p = HT_HASH_TO_BUCKET_EX(arData, idx); if (p->key == key) { /* check for the same interned string */ return p; } while (1) { if (p->h == ZSTR_H(key) && EXPECTED(p->key) && zend_string_equal_content(p->key, key)) { return p; } idx = Z_NEXT(p->val); if (idx == HT_INVALID_IDX) { return NULL; } p = HT_HASH_TO_BUCKET_EX(arData, idx); if (p->key == key) { /* check for the same interned string */ return p; } } } /* }}} */ /* {{{ void igbinary_zend_hash_do_resize(HashTable *ht) */ static void ZEND_FASTCALL igbinary_zend_hash_do_resize(HashTable *ht) { // IS_CONSISTENT(ht); // HT_ASSERT_RC1(ht); if (ht->nNumUsed > ht->nNumOfElements + (ht->nNumOfElements >> 5)) { /* additional term is there to amortize the cost of compaction */ zend_hash_rehash(ht); } else if (ht->nTableSize < HT_MAX_SIZE) { /* Let's double the table size */ void *new_data, *old_data = HT_GET_DATA_ADDR(ht); uint32_t nSize = ht->nTableSize + ht->nTableSize; Bucket *old_buckets = ht->arData; ht->nTableSize = nSize; #if PHP_VERSION_ID >= 70300 new_data = pemalloc(HT_SIZE_EX(nSize, HT_SIZE_TO_MASK(nSize)), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT); #else new_data = pemalloc(HT_SIZE(ht), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT); #endif ht->nTableMask = HT_SIZE_TO_MASK(ht->nTableSize); HT_SET_DATA_ADDR(ht, new_data); memcpy(ht->arData, old_buckets, sizeof(Bucket) * ht->nNumUsed); pefree(old_data, GC_FLAGS(ht) & IS_ARRAY_PERSISTENT); zend_hash_rehash(ht); } else { zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%u * %zu + %zu)", ht->nTableSize * 2, sizeof(Bucket) + sizeof(uint32_t), sizeof(Bucket)); } } /* }}} */ static void ZEND_FASTCALL zend_hash_packed_grow(HashTable *ht) /* {{{ */ { // HT_ASSERT_RC1(ht); if (ht->nTableSize >= HT_MAX_SIZE) { zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%u * %zu + %zu)", ht->nTableSize * 2, sizeof(Bucket), sizeof(Bucket)); } ht->nTableSize += ht->nTableSize; HT_SET_DATA_ADDR(ht, perealloc2(HT_GET_DATA_ADDR(ht), HT_SIZE_EX(ht->nTableSize, HT_MIN_MASK), HT_USED_SIZE(ht), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT)); } /* }}} */ static zend_always_inline Bucket *zend_hash_index_find_bucket(const HashTable *ht, zend_ulong h) /* {{{ */ { uint32_t nIndex; uint32_t idx; Bucket *p, *arData; arData = ht->arData; nIndex = h | ht->nTableMask; idx = HT_HASH_EX(arData, nIndex); while (idx != HT_INVALID_IDX) { ZEND_ASSERT(idx < HT_IDX_TO_HASH(ht->nTableSize)); p = HT_HASH_TO_BUCKET_EX(arData, idx); if (p->h == h && !p->key) { return p; } idx = Z_NEXT(p->val); } return NULL; } /* }}} */ /* Methods used by igbinary.c */ /* {{{ zval *igbinary_zend_hash_add_or_find(HashTable *ht, zend_string *key) */ /* Source: zend_hash_add_or_update_i with adaptions */ static zend_always_inline zval *igbinary_zend_hash_add_or_find(HashTable *ht, zend_string *key) { zend_ulong h; uint32_t nIndex; uint32_t idx; Bucket *p, *arData; // IS_CONSISTENT(ht); // HT_ASSERT_RC1(ht); #if PHP_VERSION_ID >= 70400 if (UNEXPECTED(HT_FLAGS(ht) & (HASH_FLAG_UNINITIALIZED|HASH_FLAG_PACKED))) #else if (UNEXPECTED(((HT_FLAGS(ht) ^ HASH_FLAG_INITIALIZED) & (HASH_FLAG_INITIALIZED|HASH_FLAG_PACKED)))) #endif { #if PHP_VERSION_ID >= 70400 if (EXPECTED(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED)) #else if (EXPECTED(!(HT_FLAGS(ht) & HASH_FLAG_INITIALIZED))) #endif { zend_hash_real_init_mixed(ht); if (!ZSTR_IS_INTERNED(key)) { zend_string_addref(key); HT_FLAGS(ht) &= ~HASH_FLAG_STATIC_KEYS; zend_string_hash_val(key); } goto add_to_hash; } else { zend_hash_packed_to_hash(ht); if (!ZSTR_IS_INTERNED(key)) { zend_string_addref(key); HT_FLAGS(ht) &= ~HASH_FLAG_STATIC_KEYS; zend_string_hash_val(key); } } } else { p = igbinary_zend_hash_find_bucket(ht, key, 0); if (p) { return &p->val; } if (!ZSTR_IS_INTERNED(key)) { zend_string_addref(key); HT_FLAGS(ht) &= ~HASH_FLAG_STATIC_KEYS; } } /* If the Hash table is full, resize it */ if ((ht)->nNumUsed >= (ht)->nTableSize) { igbinary_zend_hash_do_resize(ht); } add_to_hash: idx = ht->nNumUsed++; ht->nNumOfElements++; #if PHP_VERSION_ID < 70300 if (ht->nInternalPointer == HT_INVALID_IDX) { ht->nInternalPointer = idx; } zend_hash_iterators_update(ht, HT_INVALID_IDX, idx); #endif arData = ht->arData; p = arData + idx; p->key = key; p->h = h = ZSTR_H(key); nIndex = h | ht->nTableMask; Z_NEXT(p->val) = HT_HASH_EX(arData, nIndex); HT_HASH_EX(arData, nIndex) = HT_IDX_TO_HASH(idx); ZVAL_NULL(&p->val); return &p->val; } /* }}} */ static zend_always_inline zval *igbinary_zend_hash_index_add_or_find(HashTable *ht, zend_ulong h) /* {{{ */ { uint32_t nIndex; uint32_t idx; Bucket *p; // IS_CONSISTENT(ht); // HT_ASSERT_RC1(ht); if (HT_FLAGS(ht) & HASH_FLAG_PACKED) { if (h < ht->nNumUsed) { p = ht->arData + h; if (Z_TYPE(p->val) != IS_UNDEF) { replace: return &p->val; } else { /* we have to keep the order :( */ goto convert_to_hash; } } else if (EXPECTED(h < ht->nTableSize)) { add_to_packed: p = ht->arData + h; /* incremental initialization of empty Buckets */ if (h > ht->nNumUsed) { Bucket *q = ht->arData + ht->nNumUsed; while (q != p) { ZVAL_UNDEF(&q->val); q++; } } ht->nNextFreeElement = ht->nNumUsed = h + 1; goto add; } else if ((h >> 1) < ht->nTableSize && (ht->nTableSize >> 1) < ht->nNumOfElements) { zend_hash_packed_grow(ht); goto add_to_packed; } else { if (ht->nNumUsed >= ht->nTableSize) { ht->nTableSize += ht->nTableSize; } convert_to_hash: zend_hash_packed_to_hash(ht); } #if PHP_VERSION_ID >= 70400 } else if (HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED) { #else } else if (!(HT_FLAGS(ht) & HASH_FLAG_INITIALIZED)) { #endif if (h < ht->nTableSize) { zend_hash_real_init_packed(ht); goto add_to_packed; } zend_hash_real_init_mixed(ht); } else { p = zend_hash_index_find_bucket(ht, h); if (p) { goto replace; } /* If the Hash table is full, resize it */ if ((ht)->nNumUsed >= (ht)->nTableSize) { igbinary_zend_hash_do_resize(ht); } } idx = ht->nNumUsed++; nIndex = h | ht->nTableMask; p = ht->arData + idx; Z_NEXT(p->val) = HT_HASH(ht, nIndex); HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(idx); if ((zend_long)h >= ht->nNextFreeElement) { ht->nNextFreeElement = (zend_long)h < ZEND_LONG_MAX ? h + 1 : ZEND_LONG_MAX; } add: ht->nNumOfElements++; #if PHP_VERSION_ID < 70300 if (ht->nInternalPointer == HT_INVALID_IDX) { ht->nInternalPointer = ht->nNumUsed - 1; } zend_hash_iterators_update(ht, HT_INVALID_IDX, ht->nNumUsed - 1); #endif p->h = h; p->key = NULL; ZVAL_NULL(&p->val); return &p->val; } /* }}} */ #endif /* PHP 7.2 to 8.0 */ /* * Local variables: * tab-width: 2 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ #endif /* IGBINARY_ZEND_HASH */ igbinary-3.2.13/src/php7/php_igbinary.h0000664000175000017500000000373214366750307017031 0ustar tysontyson/* +----------------------------------------------------------------------+ | See COPYING file for further copyright information | +----------------------------------------------------------------------+ | Author: Oleg Grenrus | | See CREDITS for contributors | +----------------------------------------------------------------------+ */ #ifndef PHP_IGBINARY_H #define PHP_IGBINARY_H // Note: php_igbinary.h should contain publicly exposed variables, functions, and macros of igbinary. // If a macro is needed by *only* igbinary, put it in igbinary_macros.h #include "php.h" /** Module entry of igbinary. */ extern zend_module_entry igbinary_module_entry; #define phpext_igbinary_ptr &igbinary_module_entry #ifdef PHP_WIN32 #define PHP_IGBINARY_API __declspec(dllexport) #else #define PHP_IGBINARY_API #endif ZEND_BEGIN_MODULE_GLOBALS(igbinary) zend_bool compact_strings; ZEND_END_MODULE_GLOBALS(igbinary) #ifdef ZTS #include "TSRM.h" #endif #include "ext/standard/php_smart_string.h" /** Module init function. */ PHP_MINIT_FUNCTION(igbinary); /** Module shutdown function. */ PHP_MSHUTDOWN_FUNCTION(igbinary); /** Request init function. */ PHP_RINIT_FUNCTION(igbinary); /** Request shutdown function. */ PHP_RSHUTDOWN_FUNCTION(igbinary); /** Module info function for phpinfo(). */ PHP_MINFO_FUNCTION(igbinary); /** string igbinary_serialize(mixed value). * Returns the binary serialized value. */ PHP_FUNCTION(igbinary_serialize); /** mixed igbinary_unserialize(string data). * Unserializes the given inputstring (value). */ PHP_FUNCTION(igbinary_unserialize); #ifdef ZTS #define IGBINARY_G(v) TSRMG(igbinary_globals_id, zend_igbinary_globals *, v) #else #define IGBINARY_G(v) (igbinary_globals.v) #endif #endif /* PHP_IGBINARY_H */ /* * Local variables: * tab-width: 2 * c-basic-offset: 0 * End: * vim600: noet sw=2 ts=2 fdm=marker * vim<600: noet sw=2 ts=2 */ igbinary-3.2.13/src/php7/ig_win32.h0000664000175000017500000000116314366750307015773 0ustar tysontyson#ifndef _IG_WIN32_H #define _IG_WIN32_H #if PHP_WIN32 # if defined(_MSC_VER) && _MSC_VER >= 1800 # include # include # else # include "win32/php_stdint.h" # ifndef inline # define inline __inline # endif # ifndef __cplusplus # if !0 typedef enum { false = 0, true = 1 } _Bool; # define bool _Bool # endif # else typedef bool _Bool; # define bool _Bool # endif # define false 0 # define true 1 # define __bool_true_false_are_defined 1 # endif /* __MSC_VER */ # ifdef _DEBUG # include # endif #endif /* PHP_WIN32 */ #endif /* _IG_WIN32_H */ igbinary-3.2.13/igbinary.php0000664000175000017500000000673014366750307015056 0ustar tysontyson * @version 1.0.0 * @package igbinary */ /** * Generates a storable representation of a value. * This is useful for storing or passing PHP values around without losing their type and structure. * To make the serialized string into a PHP value again, use {@link igbinary_unserialize}. * * igbinary_serialize() handles all types, except the resource-type. * You can even serialize() arrays that contain references to itself. * Circular references inside the array/object you are serialize()ing will also be stored. * * If object implements {@link http://www.php.net/~helly/php/ext/spl/interfaceSerializable.html Serializable} -interface, * PHP will call the member function serialize to get serialized representation of object. * * When serializing objects, PHP will attempt to call the member function __sleep prior to serialization. * This is to allow the object to do any last minute clean-up, etc. prior to being serialized. * Likewise, when the object is restored using unserialize() the __wakeup member function is called. * * @param mixed $value The value to be serialized. * @return string Returns a string containing a binary representation of value that can be stored anywhere. * @link http://www.php.net/serialize PHP's default serialize */ function igbinary_serialize($value); /** Creates a PHP value from a stored representation. * igbinary_unserialize() takes a single serialized variable and converts it back into a PHP value. * * If the variable being unserialized is an object, * then after successfully reconstructing the object, * PHP will automatically call the __wakeup() member function (if it exists). * * If the passed in string could not be unserialized, * then NULL is returned and an E_WARNING is issued. * * @param string $str The serialized string. * @return mixed The unserialized value is returned. It can be a boolean, integer, float, string, array, object or null. * @link http://www.php.net/manual/en/function.unserialize.php PHP's default unserialize * @link https://secure.php.net/serializable Serializable interface */ function igbinary_unserialize($str); igbinary-3.2.13/igbinary.php.ini0000644000175000017500000000021014366750307015615 0ustar tysontyson[igbinary] extension=igbinary.so ; Enable or disable compacting of duplicate strings ; The default is On. ;igbinary.compact_strings=On igbinary-3.2.13/tags.sh0000755000175000017500000000032314366750307014024 0ustar tysontyson#!/bin/sh # # This generates the tags file for vim or emacs. (vim -t igbinary_serialize8) # # Try with "vim -t igbinary_serialize8" # find . -name "*.h" -o -name "*.c" | ctags-exuberant --language-force=c -L - igbinary-3.2.13/igbinary.spec0000644000175000017500000000310614366750307015211 0ustar tysontyson# Define version and release number %define version 1.0.2 %define release 1 Name: php-igbinary Version: %{version} Release: %{release}%{?dist} Packager: Mikko Koppanen Summary: PHP igbinary extension License: PHP Style License (http://opensource.dynamoid.com/#license) Group: Web/Applications URL: http://opensource.dynamoid.com/ # pear package creates .tgz file and the original source was .tar.gz Source: http://opensource.dynamoid.com/igbinary-%{version}.tgz Prefix: %{_prefix} Buildroot: %{_tmppath}/%{name}-%{version}-%{release}-root BuildRequires: php-devel, make, gcc, /usr/bin/phpize %description Igbinary is a drop in replacement for the standard PHP serializer. Instead of time and space consuming textual representation, igbinary stores PHP data structures in a compact binary form. %prep %setup -q -n igbinary-%{version} %build /usr/bin/phpize && %configure && %{__make} %{?_smp_mflags} # Clean the buildroot so that it does not contain any stuff from previous builds [ "%{buildroot}" != "/" ] && %{__rm} -rf %{buildroot} # Install the extension %{__make} install INSTALL_ROOT=%{buildroot} # Create the ini location %{__mkdir} -p %{buildroot}/etc/php.d # Preliminary extension ini echo "extension=igbinary.so" > %{buildroot}/%{_sysconfdir}/php.d/igbinary.ini %clean [ "%{buildroot}" != "/" ] && %{__rm} -rf %{buildroot} %files %{_libdir}/php/modules/igbinary.so %{_sysconfdir}/php.d/igbinary.ini %{_includedir}/php/ext/igbinary/igbinary.h %changelog * Fri Oct 02 2009 Mikko Koppanen - Initial spec file igbinary-3.2.13/COPYING0000644000175000017500000000301714366750307013565 0ustar tysontysonCopyright (c) 2008 Sulake Dynamoid Oy, 2008-2014 Oleg Grenrus, Teddy Grenman, igbinary contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the 'igbinary' nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. igbinary-3.2.13/CREDITS0000664000175000017500000000104514366750307013553 0ustar tysontysonigbinary is written for IRC-Galleria by Sulake Dynamoid Oy and its employees. Author * Oleg Grenrus Contributors * Teddy Grenman (fixes, tests, docs) * Kari Lavikka (docs) * Pierre Joye (Windows support) * Mikko Koppanen (PECL and RPM spec files) * Tyson Andre (fixes, tests, docs, php 7.x support) Other * Original hash functions - Bob Jenkins --- http://irc-galleria.net/ - a finnish social networking site igbinary-3.2.13/README.md0000664000175000017500000001441314366750307014015 0ustar tysontysonigbinary ======== [![Build Status](https://github.com/igbinary/igbinary/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/igbinary/igbinary/actions/workflows/main.yml?query=branch%3Amaster) [![Build Status (Windows)](https://ci.appveyor.com/api/projects/status/suhkkumj1yh9dgan?svg=true)](https://ci.appveyor.com/project/TysonAndre/igbinary-bemsx) Igbinary is a drop in replacement for the standard php serializer. Instead of the time and space consuming textual representation used by PHP's `serialize`, igbinary stores php data structures in a compact binary form. Memory savings are significant when using memcached, APCu, or similar memory based storages for serialized data. The typical reduction in storage requirements are around 50%. The exact percentage depends on your data. Unserialization performance is [at least on par with the standard PHP serializer, and is much faster for repetitive data](benchmark/comparisons.php). Serialization performance depends on the `igbinary.compact_strings` option which enables duplicate string tracking. String are inserted to a hash table, which adds some overhead when serializing. In usual scenarios this does not have much of an impact, because the typical usage pattern is "serialize rarely, unserialize often". With the `compact_strings` option enabled, igbinary is usually a bit slower than the standard serializer. Without it, igbinary is a bit faster. Features -------- - Support for the same data types as the standard PHP serializer: null, bool, int, float, string, array and object. - `__autoload` & `unserialize_callback_func` - `__sleep` & `__wakeup` - `__serialize` & `__unserialize` (only used in php 7.4+) - Serializable -interface - Data portability between platforms (32/64bit, endianness) - Tested on Linux amd64, Linux ARM, Mac OSX x86, HP-UX PA-RISC and NetBSD sparc64 - Hooks up to the APCu in-memory key-value store as a serialization handler. - Compatible with 7.0 – 8.0 (The older igbinary [2.x releases](https://github.com/igbinary/igbinary/tree/v2) support 5.2 – 5.6, 7.0 – 7.3) Implementation details ---------------------- Storing complex PHP data structures such as arrays of associative arrays with the standard PHP serializer is not very space efficient. The main reasons of this inefficiency are listed below, in order of significance (at least in our applications): 1. Array keys, property names, and class names are repeated redundantly. 2. Numerical values are plain text. 3. Human readability adds some overhead. Igbinary uses two strategies to minimize the size of the serialized output. 1. Repeated strings are stored only once (this also includes class and property names). Collections of objects benefit significantly from this. See the `igbinary.compact_strings` option. 2. Integer values are stored in the smallest primitive data type available: *123* = `int8_t`, *1234* = `int16_t`, *123456* = `int32_t` ... and so on. 3. ( Well, it is not human readable ;) How to use ---------- Add the following lines to your php.ini: ```ini ; Load igbinary extension extension=igbinary.so ; Use igbinary as session serializer session.serialize_handler=igbinary ; Enable or disable compacting of duplicate strings ; The default is On. igbinary.compact_strings=On ; If uncommented, use igbinary as the serializer of APCu ; (APCu 5.1.10 or newer is strongly recommended) ;apc.serializer=igbinary ``` Then, in your php code, replace `serialize` and `unserialize` function calls with [`igbinary_serialize` and `igbinary_unserialize`](./igbinary.php). Installing ---------- ### Linux If PHP was installed through your package manager, the package manager may also contain prebuilt packages for `igbinary` (with a package name similar to php-igbinary). - The packages from some package managers and OS versions may be out of date and have known bugs. The latest release of igbinary is [![The Latest Stable Version](https://img.shields.io/github/v/release/igbinary/igbinary.svg)](https://github.com/igbinary/igbinary/releases) Igbinary may also be installed with the command `pecl install igbinary` (You will need to enable igbinary in php.ini) Alternately, you may wish to [build from source](#building-from-source) ### MacOS `pecl install igbinary` is the recommended installation method (You will need to enable igbinary in php.ini) Alternately, you may wish to [build from source](#building-from-source). ### Installing on Windows Prebuilt DLLs can be [downloaded from PECL](https://pecl.php.net/package/igbinary). If you are a contributor to/packager of igbinary, or need to build from source, see [WINDOWS.md](./WINDOWS.md) ### Building from source 1. `phpize` 2. `./configure` - With GCC: `./configure CFLAGS="-O2 -g" --enable-igbinary` - With ICC (Intel C Compiler) `./configure CFLAGS=" -no-prec-div -O3 -xO -unroll2 -g" CC=icc --enable-igbinary` - With clang: `./configure CC=clang CFLAGS="-O0 -g" --enable-igbinary` 3. `make` 4. `make test` 5. `make install` 6. `igbinary.so` is installed to the default extension directory Bugs & Contributions -------------------- Mailing list for bug reports and other development discussion can be found at http://groups.google.com/group/igbinary (no longer used) File bug reports at https://github.com/igbinary/igbinary/issues The preferred way to contribute is with pull requests. Feel free to fork this at http://github.com/igbinary/igbinary See [TESTING.md](./TESTING.md) for advice for testing patches. See [TECH\_NOTES.md](./TECH_NOTES.md) for information about how igbinary is implemented Utilizing in other extensions ----------------------------- Igbinary can be called from other extensions fairly easily. Igbinary installs its header file to _ext/igbinary/igbinary.h_. There are just two straightforward functions: `igbinary_serialize` and `igbinary_unserialize`. Look at _igbinary.h_ for prototypes and usage. Add `PHP_ADD_EXTENSION_DEP(yourextension, igbinary)` to your _config.m4_ in case someone wants to compile both of them statically into php. Trivia ------ Where does the name "igbinary" come from? There was once a similar project called fbinary but it has disappeared from the Internet a long time ago. Its architecture wasn't particularly clean either. IG is an abbreviation for a Finnish social networking site IRC-Galleria (http://irc-galleria.net/) igbinary-3.2.13/TECH_NOTES.md0000664000175000017500000001632014366750307014552 0ustar tysontysonOverview -------- The implementation of `igbinary` is largely based on php's own serializer. Refer to https://github.com/php/php-src/blob/master/ext/standard/var_unserializer.re and https://github.com/php/php-src/blob/master/ext/standard/var.c Format ------ Refer to the implementation for details of how this works - this section is an overview and is not comprehensive. Values are serialized with a byte representing the type of the value, followed by 0 or more bytes with the serialization of the value. Values can also be references to reuse earlier values - `igbinary_type_string_id8` refers to a string value that was already serialized, where the identifier of that string takes up 8 bits(1 byte) - `igbinary_type_objref8` refers to the *array of properties in* an object value that was already serialized - `igbinary_type_object_id8` refers to a object that was already serialized. - `igbinary_type_ref8` refers to something of any type that's part of a PHP reference group. `igbinary_type_ref` creates a reference group the first time it's used, and `igbinary_type_ref8` adds a value that's a reference to that reference group. The header returned by `igbinary_serialize()` in the latest igbinary releases always starts with `\x00\x00\x00\x02` (version 2) (long ago, this started with `\x00\x00\x00\x01`) ```c // From src/php7/igbinary.c enum igbinary_type { /* 00 */ igbinary_type_null, /**< Null. */ /* 01 */ igbinary_type_ref8, /**< Array reference. */ /* 02 */ igbinary_type_ref16, /**< Array reference. */ /* 03 */ igbinary_type_ref32, /**< Array reference. */ /* 04 */ igbinary_type_bool_false, /**< Boolean true. */ /* 05 */ igbinary_type_bool_true, /**< Boolean false. */ /* 06 */ igbinary_type_long8p, /**< Long 8bit positive. */ /* 07 */ igbinary_type_long8n, /**< Long 8bit negative. */ /* 08 */ igbinary_type_long16p, /**< Long 16bit positive. */ /* 09 */ igbinary_type_long16n, /**< Long 16bit negative. */ /* 0a */ igbinary_type_long32p, /**< Long 32bit positive. */ /* 0b */ igbinary_type_long32n, /**< Long 32bit negative. */ /* 0c */ igbinary_type_double, /**< Double. */ /* 0d */ igbinary_type_string_empty, /**< Empty string. */ /* 0e */ igbinary_type_string_id8, /**< String id. */ /* 0f */ igbinary_type_string_id16, /**< String id. */ /* 10 */ igbinary_type_string_id32, /**< String id. */ /* 11 */ igbinary_type_string8, /**< String. */ /* 12 */ igbinary_type_string16, /**< String. */ /* 13 */ igbinary_type_string32, /**< String. */ /* 14 */ igbinary_type_array8, /**< Array. */ /* 15 */ igbinary_type_array16, /**< Array. */ /* 16 */ igbinary_type_array32, /**< Array. */ /* 17 */ igbinary_type_object8, /**< Object. */ /* 18 */ igbinary_type_object16, /**< Object. */ /* 19 */ igbinary_type_object32, /**< Object. */ /* 1a */ igbinary_type_object_id8, /**< Object string id. */ /* 1b */ igbinary_type_object_id16, /**< Object string id. */ /* 1c */ igbinary_type_object_id32, /**< Object string id. */ /* 1d */ igbinary_type_object_ser8, /**< Object serialized data. (when Serializable::serialize() is used) */ /* 1e */ igbinary_type_object_ser16, /**< Object serialized data. */ /* 1f */ igbinary_type_object_ser32, /**< Object serialized data. */ /* 20 */ igbinary_type_long64p, /**< Long 64bit positive. */ /* 21 */ igbinary_type_long64n, /**< Long 64bit negative. */ /* 22 */ igbinary_type_objref8, /**< Object reference. */ /* 23 */ igbinary_type_objref16, /**< Object reference. */ /* 24 */ igbinary_type_objref32, /**< Object reference. */ /* 25 */ igbinary_type_ref, /**< Simple reference */ }; ``` For example, `bin2hex(igbinary_serialize(['first', true]))` is `000000021402060011056669727374060105` ``` 00000002 -- 4 byte header indicating this is igbinary serialized data, version 2 14 02 -- An array(igbinary_type_array8) of size 2 (8-bit length) 06 00 -- The first array key - an igbinary_type_long8p (positive unsigned 8-bit integer) with value `0` (index 0) 11 05 6669727374 -- The array value - igbinary_type_string8 with an 8-bit length of 5 and the string value `first` 06 01 -- The second array key - the value `0` 05 -- igbinary_type_bool_true representing the value `true` ``` A limitation of the current serialization format and unserializers is that there can only be one reference group to the same value. (`igbinary_type_ref`) For example, `$b = $a; igbinary_serialize([&$a, &$a, &$b, &$b])` would be serialized the same way as `[&$a, &$a, &$a, &$a]`. Serializing ----------- See README.md for details about the format, performance, and ini options. ### Edge cases The following types of PHP methods are invoked when serializing values. `igbinary_serialize()` must ensure that the pointers it keeps to the values being serialized (and temporary values returned by invoked methods) aren't modified during serialization. 1. `Serializable::serialize()` may have side effects if the serialized object has 2. `__sleep` is called immediately, which may have side effects 3. `__serialize` may have immediate side effects. 4. The serializers of internal classes. Edge cases are dealt with by adding a reference to all referenceable values so that they can't be freed prematurely. Unserializing ------------- ### Edge cases `igbinary_unserialize()` must ensure that the pointers it keeps to the php values it creates don't become invalid during unserialization. The following types of magic methods can be called during unserialization: 1. `Serializable::unserialize(string $serialized)` may have global side effects, but is assumed to be unable to read or modify other values that are being unserialized (excluding values created during other calls to `Serializable::unserialize()`, but igbinary is only concerned with the resulting object instance) because it must be called while unserializing everything else. 2. `__unserialize()` is expected to be safe, because everything else has already been unserialized before it gets called. 3. `__wakeup()` is expected to be safe, because everything else has already been unserialized before it gets called. 4. `__destruct()` is called, but only if&after everything was successfully unserialized (e.g. if `__wakeup()` throws, `__destruct()` will not be called for any directly created objects) Sessions -------- The standard implementation of `igbinary_serialize` and `igbinary_unserialize` are used to serialize/unserialize session data. Refer to https://github.com/php/php-src/blob/master/ext/session/session.c for reference implementations of `PS_SERIALIZER_ENCODE_FUNC` and `PS_SERIALIZER_DECODE_FUNC` and how they change in PHP minor versions. APCu ---- `apc_register_serializer` is called if igbinary was called with APCu support to make APCu aware that the igbinary serializer is available to serialize data to store in memory. See the parts of the code referring to `HAVE_APCU_SUPPORT` Redis, Memcached, etc. ---------------------- These data stores often use binary flags (or any other unambiguous indicator) to indicate that the igbinary serializer was used when saving data to the database, and read those flags to determine which unserializer (e.g. `json_decode()`, `unserialize()`, `igbinary_unserialize()`, msgpack) to use. igbinary-3.2.13/NEWS0000664000175000017500000002653514366750307013245 0ustar tysontyson3.2.8 2022-10-17 ======= * Fix invalid release artifact name in job to build dlls for https://github.com/igbinary/igbinary 3.2.8 2022-10-16 ======= * Reduce excessive inlining to reduce shared library size. * Miscellaneous optimizations. * Set up CI job to build dlls on https://github.com/igbinary/igbinary - at the moment, the infrastructure used by the Windows for php team has been broken for months. 3.2.7 2022-01-12 ======= * Update test expectations for php 8.2.0-dev. Add `#[AllowDynamicProperties]` Attribute to some tests to avoid notices. * In php 8.1+, make igbinary_unserialize check to see if an equivalent interned string already exists when unserializing object property names, array keys, and class names and use that instead of creating a brand new string. (This deliberately doesn't create a new interned string if one doesn't already exist.) (Before this change, igbinary would deduplicate strings when serializing, but would not check if strings were interned by PHP itself when unserializing) * Avoid debug build assertion failure for `HT_ASSERT_RC1` the same way as PHP's unserialize - this is a case where ostensibly there are no other references to the array being unserialized. 3.2.6 2021-08-11 ======= * Fix igbinary version found in Reflection. 3.2.5 2021-08-07 ======= * Fix change in behavior introduced in 3.2.2RC1 when unserializing arrays - the internal array pointer (for `next()`, `key()`, etc) pointed past the end of the array in php 7.0-7.2. 3.2.4 2021-07-24 ======= * Forbid serializing classes that deny serialization/unserialization (anonymous classes, CURLFile, etc.) even when subclasses implement '__serialize' and '__unserialize' 3.2.3 2021-06-09 ======= * Fix build for php 8.1 after changes to enum internals. * Update tests to suppress deprecations in php 8.1 and support run-tests.php changes in php 8.1 * Don't emit a notice when unserialize_callback_func causes igbinary_unserialize to throw https://bugs.php.net/bug.php?id=81118 3.2.2 2021-04-18 ======= * Eliminate impossible/redundant checks. * Add a new type code for serialization and unserialization of PHP strings that are larger than 4GB. * Add additional checks for overflow when serializing extremely large data structures. (e.g. serializing more than 2**32 strings or 2**32 objects/references/arrays) * Support serializing and unserializing php 8.1 enums (can only be unserialized in php 8.1+) 3.2.2RC1 2021-01-11 ======= * Update php version check to allow igbinary to be statically built in PHP 8.0+ * Fix bug in out of memory error handling in __sleep, slightly speed up serializing with __sleep. * Continue serializing remaining properties if a missing property name is returned from __sleep. * Speed up serializing by optimizing for the case where there is no memory manager override. When there is a memory manager override, only use that for allocating the string to return. (benchmarks/serialize-scalar-int.b.php showed a speedup from 0.22 to 0.18 seconds for repeated serialization of a single scalar, and from 0.186 to 0.180 seconds for benchmarks/serialize-stringarray.b.php for an array of strings) * Speed up unserializing arrays in php 7.2-8.0 by adding optimized code for finding the hash bucket of a string/integer key of an array, or creating a placeholder if it does not already exist. 3.2.1 2020-12-27 ======= * Fix crash when unserializing if __serialize was defined but __unserialize was undefined in php 8.0+ (due to typo). 3.2.0 2020-12-26 ======= * Use PHP's shared empty array instance when unserializing empty arrays in php 7.3+. (helps slightly with memory usage when repeatedly unserializing, when removing elements from arrays before unserializing them, or when serializing values including an empty array that was unserialized) * Emit a deprecation notice when serializing resources. PHP itself is converting many resources to objects that throw an Error on serialization attempts. Continue to represent resources as null in the serialized data. * Fix memory management bug when unserializing invalid data (duplicate properties in objects (e.g. from `__sleep`) or duplicate fields in arrays (impossible for valid data)). * Speed up calls to `__serialize`/`__unserialize` in php 8.0+. * Fix error messages for unserialize_callback_func: make messages properly refer to the autoload function. * Optimize unserializing alternative names for private/protected properties that were previously public. 3.1.6 2020-10-08 ======= * Fix build failure with older C standard (e.g. building on CentOS 6). * Otherwise, identical to 3.1.6RC1. 3.1.6RC1 2020-10-07 ======= * Fix igbinary_serialize incorrectly deduplicating arrays/objects/references when they were garbage collected/freed during serialization. 3.1.5 2020-09-02 ======= * Update unit test expectation to match behavior in php 8 due to changes in php's handling of cyclic references in arrays. * Support API changes in php 8.0.0beta3. 3.1.4 2020-07-05 ======= * Fix unserialization of PHP references to internal/user-defined classes using PHP 7.4's `__unserialize` (e.g. `ArrayObject`) 3.1.3 2020-07-04 ======= * Properly serialize reference groups of size 1 (these can be created by array_walk_recursive and other functions). Note that this does not fix the general case where values not being serialized are in the same reference group as a value being serialized. * PHP 8.0 compatibility fixes. 3.1.2 2020-01-16 ======= * Speed up object, array, reference, and string serialization. * Speed up unserializing integers between 0 and 65535 (as values and array keys). * Speed up unserializing objects with declared properties. 3.1.1 2020-01-16 ======= * Fix bug causing incorrect serialization for 1 in 2**32 strings on 64-bit php installations when string hashes collide. (https://github.com/igbinary/igbinary/issues/260) 3.1.1a1 2020-01-11 ======= * Throw when an uninitialized php 7.4 typed property is included in the result of __sleep(), instead of emitting a notice and attempting to represent the unset/uninitialized value as null (#258). See https://bugs.php.net/bug.php?id=79002 Uninitialized properties without types (from __sleep) continue to cause igbinary to emit notices and are represented as null. 3.1.0 2019-12-27 ======= * Same as 3.1.0b4. 3.1.0b4 2019-12-20 ======= * Don't call __destruct for objects where deferred __unserialize calls were not started (e.g. due to Serializable::unserialize throwing). 3.1.0b3 2019-12-10 ======= * Skip over object properties that are uninitialized or unset when serializing, instead of serializing them as null. This is done to avoid Errors when unserializing their values for php 7.4 typed properties. 3.1.0b2 2019-12-09 ======= * Fix crashes related to unserializing instances of classes with php 7.4 typed properties. 3.1.0b1 2019-12-08 ======= * Support php 7.4's __serialize/__unserialize the same way serialize()/unserialize() does. This deliberately only supports __serialize/__unserialize in php 7.4, to making switching to/from serialize()/unserialize() as straightforward as possible. 3.0.1 2019-03-20 ======= * Fix version check when statically building igbinary inside of the php-src folder. 3.0.0 2019-02-17 ======= * Identical to 3.0.0a2 3.0.0a2 2019-02-13 ======= * Don't use empty string for serializing empty $_SESSION array, it breaks some save handlers. (Issue #231) Continue treating the empty string as the empty $_SESSION array when unserializing. 3.0.0a1 2019-02-08 ======= * This release line drops support for PHP5. * Drop support for APC (APC was only available for PHP5 - It is the predecessor of APCu) * Emit a warning and return null if igbinary_unserialize() is passed more data to unserialize than expected. * Fix compilation against PHP 7.4-dev. Igbinary does NOT yet properly serialize/unserialize all classes with PHP 7.4-dev's typed properties. * The serialization format is exactly the same as igbinary 2.x 2.0.8 2018-10-20 ======= * Be more aggressive about deduplication when generating serialization of arrays in php 7.0+. * Define HAVE_IGBINARY on Unix/Linux. (previously defined only on Windows) * Update formatting/wording of documentation. 2.0.7 2018-06-27 ======= * Fix compiler warnings about format strings, for errors that should not occur during normal igbinary usage. 2.0.6 2018-05-12 ======= * Same as 2.0.6RC1 2.0.6RC1 2018-04-01 ======= * Fix a bug in Windows debug builds. * Emit more specific warnings when __sleep() returns a declared property that was unset. * Fix harmless compiler warnings during builds. * Fix a build error on PHP7.3-dev. 2.0.5 2017-11-04 ======== * Same as 2.0.5RC1 (no bugs were reported in that release candidate) 2.0.5RC1 2017-10-15 ======== * Improve performance when unserializing objects/arrays and serializing objects/arrays/strings in php 5/7. * Update unserialization of integer object keys for php 7.2: Make those keys accessible when unserializing. * Properly pick up presence of gcc for default compiler flags (`cc --version` doesn't contain gcc). Add -O2 to default gcc compiler flags. * Use empty string for serializing empty $_SESSION array, similar to "session.serialize_handler=php". Older igbinary releases already unserialize the empty string to the empty array. 2.0.4 2017-04-14 ======== * Fixes bug #129: Should not call __wakeup() on data which was created by Serializable::unserialize() 2.0.3 2017-03-31 ======== * Fixes bug #126: Fatal error: "igbinary_serialize_zval: zval has unknown type 0" (IS_UNDEF) Make this a warning instead of a fatal error (and serialize as null instead), since IS_UNDEF is a known type. Later releases will fix the root cause of the warning, and consistently omit array/object/other entries for IS_UNDEF. 2.0.2 2017-02-27 ======== * Compatible with PHP 5.2 - 7.1 * Fixes crash in Memcached->setMulti (in php 7.0+) when the first level of array elements have references as values. Other extensions using igbinary shouldn't be affected. 2.0.1 2016-11-19 ======== * Compatible with PHP 5.2 - 7.0 * Fixes bug in session decoder not calling __wakeup() in php 7.0+ * (Enhancement) Reuses identical strings when unserializing objects and arrays in php 7.0+ 2.0.0 2016-10-30 ======== * Compatible with PHP 5.2 - 7.0 (Adds PHP 7 support) * Serialization format is unchanged * Performance improvements for serialization and unserialization in PHP 5.2 - 7.0 * (PHP 7) Don't call __destruct if __wakeup threw an exception (or __wakeup wasn't called yet) * Ports integration with other extensions to PHP 7.0 (session serialization, Memcached, Redis, APCu, etc.) * Fixes Windows PECL builds for PHP 5.6+ * Reword warnings for invalid header bytes of serialized data (in igbinary_unserialize). 1.2.1 2014-08-29 ======== * Compatible with PHP 5.2 - 5.6 1.2.0 2014-08-28 ======== * PECL bug #22614, igbinary_unserialize(FALSE) must return FALSE * PHP bug #54662, unserializing nested objects cause crash * Other fixes 1.1.1 2011-01-17 ======== * Critical crash fix. Thanks to Ilia Alshanetsky for spotting and fixing. 1.1.0 2011-01-17 ======== * New ini setting to disable duplicate string compacting * APC serializer registration (APC 3.1.7 beta) * Windows support (PHP 5.3) * Updated serialized binary format (1.1 reads 1.0.x format) * Minor performance improvements * Bug fixes * New source repository at https://github.com/igbinary/igbinary 1.0.2 ======== * Bug fix release 1.0.1 2008-07-05 ======== * unserialize_callback_func support * slight speedup when serializing scalars 1.0.0 2008-06-25 ======== * Public version